1
f = (3, 4, 5, {3: 4}, [16, 7, 8])
g = (1, 2, [3, 4, [5, 6], {7: 8}], 9, 10, {11: f}, {12: [1, 2, {3, 4}, [5, 6]]})

I am trying to iterate recursively over g.

How to iterate each and every element recursively in python which works for any list with any level of nesting ?

I have tried with hasattr, __iter__ but won't work with unknown level of nesting.

f=(3,4,5,{3:4},[6,7,8])
g = (1, 2, [3, 4, [5, 6], {7: 8}], 9, 10, {11: (3, 4, 5, {3: 4}, [16, 7, 8])}, {12: [1, 2, set([3, 4]), [5, 6]]})
print g
for each in g:
    print each
    try:
        if hasattr(each,"__iter__"):
            for ind in each:
                print ind
                if hasattr(ind,"__iter__"):
                    for ind1 in ind:
                        print ind1
Byte Commander
  • 6,506
  • 6
  • 44
  • 71
  • What's `f` doing here? – Spacedman May 12 '16 at 06:08
  • 1
    You mention recursively but haven't actually written a recursive function - maybe try that? Also note that strings can be an issue with code like this, be careful if they may be in your input. – jonrsharpe May 12 '16 at 06:20
  • What is your desired output? If there's a dictionary, where each item consists of a key/value pair, which one to iterate over? Only the key? Only the value? Both? – Byte Commander May 12 '16 at 06:23

1 Answers1

2

To recursively iterate over something, you need of course a recursive function. Here's one that can descend into lists, tuples, sets, frozensets and the values of dictionarys. It returns a flat generator over which you can easily iterate in a single for loop:

def recursive_iterator(iterable):
    for item in iterable:

        # directly iterable types:
        if type(item) in (list, tuple, set, frozenset):
            for child_item in recursive_iterator(item):
                yield child_item

        # other iterable types where we do not want to iterate over the item directly:
        elif type(item) in (dict,):
            for child_item in recursive_iterator(item.values()):
                yield child_item

        # not iterable types which we want to return as they are:
        else: 
            yield item

Here's how you would use that function:

f = (3, 4, 5, {3: 4}, [16, 7, 8])
g = (1, 2, [3, 4, [5, 6], {7: 8}], 9, 10, {11: f}, {12: [1, 2, {3, 4}, [5, 6]]})

for x in recursive_iterator(g):
    print(x, end=" ")

The output would be this:

1 2 3 4 5 6 8 9 10 3 4 5 4 16 7 8 1 2 3 4 5 6 

See this code running on ideone.com

Byte Commander
  • 6,506
  • 6
  • 44
  • 71
  • You should really use `isinstance`, and consider using the `abc`s rather than explicitly naming types. For example, this will fail needlessly on `OrderedDict`. – jonrsharpe May 12 '16 at 12:23
  • @jonrsharpe I mainly thought to use `type` to be able to compare it with a list of types, but now I see your point. How would I compare against many items the easiest way then? `if any(map(lambda t: isinstance(item, t), [list, tuple, set, frozenset]))` should also accept subclasses of the listed types. How would your abc approach work? I have never used those yet. – Byte Commander May 12 '16 at 13:02
  • 1. `isinstance` can take a tuple of types as the second argument. 2. Read the `collections.abc` docs. – jonrsharpe May 12 '16 at 13:19
  • Thanks, will do... – Byte Commander May 12 '16 at 13:23