Search code examples
pythonfunctionpython-itertools

"TypeError: 'int' object is not iterable" when moving itertools product inside function


I'm trying to move a usage of itertools.product inside a function. When I try to do this, I am presented with the following error message and I'm not sure why:

TypeError: 'int' object is not iterable

The code is as follows. In the main function, you can see the algorithm used outside the function and then you can see the algorithm used when packaged in the function:

#!/usr/bin/env python

import itertools

def main():

    elements_specification = [[10, 20], [30, 40], [50, 60]]

    lists = [list(list_generated) for index, element_specification in enumerate(elements_specification) for list_generated in itertools.product(*elements_specification[:index + 1])]

    for list_configuration in lists:
        print(list_configuration)

    print("---")

    for list_configuration in list_element_combinations_variadic(
        [[10, 20], [30, 40], [50, 60]]
    ):
        print(list_configuration)

def list_element_combinations_variadic(
    elements_specification
    ):
    """
    This function accepts a specification of lists of elements for each place in
    lists in the form of a list, the elements of which are lists of possible
    elements and returns a list of lists corresponding to the combinations of
    elements of the specification with varying numbers of elements.

    For example, the list elements specification [[10, 20], [30, 40], [50, 60]]
    yields the following lists:

    [10]
    [20]
    [10, 30]
    [10, 40]
    [20, 30]
    [20, 40]
    [10, 30, 50]
    [10, 30, 60]
    [10, 40, 50]
    [10, 40, 60]
    [20, 30, 50]
    [20, 30, 60]
    [20, 40, 50]
    [20, 40, 60]
    """
    lists = [list(list_generated) for index, elements_specification in enumerate(elements_specification) for list_generated in itertools.product(*elements_specification[:index + 1])]
    return lists

if __name__ == "__main__":
    main()

Solution

  • Basically, you have a typo between the main method and the other one.

    In main, you correctly have element_specification in the for

    for index, element_specification in enumerate(elements_specification)
    

    But in the other method you have elements_specification in the for

    for index, elements_specification in enumerate(elements_specification) 
    

    Which just so happens to be the name of the parameter for that method, so you're reassigning that parameter in your list-comprehension


    Try this instead

    lists = [list(list_generated) for index, element in enumerate(elements_specification) for list_generated in itertools.product(*elements_specification[:index + 1])]
    return lists
    

    Or since you don't even need the element from the enumerate, just use range.

    lists = [list(list_generated) for index in range(len(elements_specification)) for list_generated in itertools.product(*elements_specification[:index + 1])]
    return lists