Qi Chen
Qi Chen

Reputation: 1718

How to handle edge case when iterating over cartesian product of sets in Python?

I have a function on zero or more Pyomo sets:

def myfunc(*sets):
    if len(sets) == 0:
         return # Do something else that is irrelevant here
    indices = reduce(lambda x, y: x * y, sets)  # Cartesian product of sets
    for i in indices:
        call_some_other_function(*i)

This fails when I pass it a single set of integers, like

import pyomo.environ
myset = pyomo.environ.Set(initialize=[1, 2])
myfunc(*myset)

because then I'm evaluating *i on an integer. What's an elegant way of handling this situation?

Upvotes: 0

Views: 268

Answers (2)

Kevin J. Chase
Kevin J. Chase

Reputation: 3956

I think you're making things harder by implementing your own Cartesian product. Python's provided itertools.product since 2.6, and it works with any number of input sets.

import itertools


def args(*args):
    return repr(args)


def for_each_index(*sets):
    if not sets:
         print('No sets given.  Doing something else.')
         return
    for index in itertools.product(*sets):
        print('Do something with ' + args(*index) + '...')
    return

I added the args function solely to show the exact result of expanding *args. You don't need it, except maybe for debugging.

Note also that there is no need to call len to test if a tuple is non-empty. if not sets: will do.

Upvotes: 0

MSeifert
MSeifert

Reputation: 152725

You can always check if it is an collections.Iterable to catch cases where it is not iterable (lists, sets, etc. are iterables - integer aren't):

from collections import Iterable
a = 1
isinstance(a, Iterable) # returns False
a = [1,2,3]
isinstance(a, Iterable) # returns True

so just do a check before you pass it into the function:

if isinstance(myset, Iterable):
    myfunc(*myset)
else:
    # something else

Upvotes: 1

Related Questions