Eric Hansen Eric Hansen -4 years ago 131
Python Question

Nested lists and tuples - building inside out

Question



Suppose that I have a list which contains two element tuples, which comprise of either further nested tuples, lists of tuples, or strings.

samp = [('coor', [('test', 'correlation'),
[('another', [('nest', 'one')]), ('tags', 'list')], ('threshold', 'foo')])]

>>> samp
[('coor',
[('test', 'correlation'),
[('another', [('nest', 'one')]), ('tags', 'list')],
('threshold', 'foo')])]


I also have a class,
Foo
, that can only properly accept a list which doesn't contain any nested lists, but can contain other
Foo
's
.

class Foo:
def __init__(self, li):
self.l = li
def __repr__(self):
return 'Foo<{0}>'.format(str(self.l))


I want to convert my nested structure
samp
into one giant valid
Foo
, so in this case it would look like

>>> big_foo
Foo<[('coor',
Foo<[('test', 'correlation'),
Foo<[('another',
Foo<[('nest', 'one')]>),
('tags', 'list')]>,
('threshold', 'foo')]>
)]>


How can I do this effectively?




My thoughts



Obviously I'm going to have to build the
Foo
objects going inside-out from the deepest-nesting lists. I know I can check if an element is a tuple or a list with

def is_seq(el):
return isinstance(el, collections.abc.Sequence) and not isinstance(el, str)


and I could check if any iterable contains a list at some level of nesting with something recursive like

def contains_list(it):
return any(isinstance(el, list) or (isinstance(el, tuple) and contains_list(el))
for el in it)


What I'm struggling with is how I will build my new structure effectively, as tuples are immutable. Recursively building the structure inside-out doesn't seem possible because tuples, and so I'm really lost for a good approach. If there's some abstraction or module which can simplify this problem for me, I would gladly accept it.




Motivation



I'm trying to wrap an R library with pyRserve, and need a serializable Python representation of nested named member lists in R. The only similar thing that pyRserve can serialize is a TaggedList which doesn't support nesting upon construction, and so now I'm left with this problem.

Answer Source

How about something like:

class Foo:
    def __init__(self, li):
        self.l = li
    def __repr__(self):
        return 'Foo<{0}>'.format(str(self.l))


def make_Foo(obj):
    if isinstance(obj, list):
        return Foo([make_Foo(item) for item in obj])
    elif isinstance(obj, tuple):
        return tuple(make_Foo(item) for item in obj)
    elif isinstance(obj, str):
        return obj
    else:
        raise Exception("Not implemented for type {}".format(type(obj)))

samp = [('coor', [('test', 'correlation'), 
        [('another', [('nest', 'one')]), ('tags', 'list')], ('threshold', 'foo')])]

x = make_Foo(samp)
print(x)

Output:

Foo<[('coor', Foo<[('test', 'correlation'), Foo<[('another', Foo<[('nest', 'one')]>), ('tags', 'list')]>, ('threshold', 'foo')]>)]>

Which, if you add some whitespace...

Foo<[('coor', 
    Foo<[('test', 'correlation'), 
        Foo<[('another', 
            Foo<[('nest', 'one')]>), 
            ('tags', 'list')]>, 
        ('threshold', 'foo')]>
    )]>

Closely resembles your desired output.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download