Search code examples
pythonclasspython-2.7instanceargument-passing

Accessing attributes of class instances passed as optional arguments


I have two class instances which have list attributes, e.g.:

foo.range = [1,2]
bar.range = [3,4]

I also have a function which takes multiple arguments:

def permutations(*args):
    return list(itertools.product(arg.range for arg in args))

I want permutations(foo, bar) to return all permutations of those two (or more) lists (i.e. [(1,3), (1,4) …]) but I actually get [([1, 2],), ([3, 4],)]

Could someone please help me to understand where am I going wrong and how to achieve the result I'm hoping for?


Solution

  • itertools.product expects the iterables as individual arguments(itertools.product(*iterables[, repeat])), so you need to unpack the items to it using the the splat(*) operator:

    >>> def permutations(*args):
            return list(itertools.product(*(arg.range for arg in args)))
    
    >>> permutations(foo, bar)
    [(1, 3), (1, 4), (2, 3), (2, 4)]
    

    Consider this simple function, it will collect all positional arguments passed to it in args:

    >>> def func(*args):
        print args
        print list(args[0])
    ...
    

    When we pass it a generator expression it is collected as one individual item in the args tuple:

    >>> func(x for x in range(5))
    (<generator object <genexpr> at 0x7fda2ab4e780>,)
    [0, 1, 2, 3, 4]
    

    To fix this we need to unpack our generator expression, also as somethine like *x for x in range(5) is not a valid syntax in Python, we need to add extra parenthesis around the generator expression:

    >>> func(*(x for x in range(5)))
    (0, 1, 2, 3, 4)
    # Failed as expected for args[0]
    Traceback (most recent call last):
      File "<ipython-input-215-13af4340dad1>", line 1, in <module>
        func(*(x for x in range(5)))
      File "<ipython-input-213-08941e2a3c06>", line 3, in func
        print list(args[0])
    TypeError: 'int' object is not iterable