在Python中嵌套列表

4

我正试图解决一个超出我的能力范围的问题。 我有一系列嵌套的列表,当迭代它们时,如果下一个元素是一个列表,我想将其作为当前元素的属性附加。 通常,示例比我的糟糕的英语更好(这里是一些可复制粘贴的代码):

class T(object):
    def __init__(self, id, children):
         self.id = id 
         self.children = children or []

    def __repr__(self):
         return u"T(id={0}, children={1})".format(self.id, self.children) 


# first a short example
l0 = [T(id=1, children=[]), T(id=2, children=[]), [T(id=3, children=[]), 
      T(id=4, children=[]), [T(id=5, children=[])]]]

正如您所看到的,l0有3个元素,最后一个是由三个元素组成的列表:我需要的是将最后一个列表附加到不是列表的前一个元素(并递归执行)

l1 = magic(l0)    
[T(id=1, children=[]), T(id=2, children=[T(id=3, children=[]), T(id=4, children=[T(id=5, children=[])])])]

希望有人可以分享一些解决方法,我已经投入了很多小时,但离解决问题还很遥远。
编辑: 为了完整起见,这里提供一个稍微复杂一点的例子。
l0 = [T(children=[], id=1),
      T(children=[], id=2),
      T(children=[], id=3),
      [T(children=[], id=40),
       T(children=[], id=41),
       T(children=[], id=42),
       T(children=[], id=43),
       T(children=[], id=44),
       T(children=[], id=45),
       [T(children=[], id=50),
        T(children=[], id=51),
        T(children=[], id=52),
        T(children=[], id=54),
        [T(children=[], id=60),
         T(children=[], id=61),
         T(children=[], id=62),
         T(children=[], id=63),
         [T(children=[], id=70)],
         T(children=[], id=64)]]],
      T(children=[], id=8),
      T(children=[], id=9)]

我以@rik-poggi的函数为例构建了一个doctest,目前看来它似乎还可以:

>>> from magic_bag import magic
>>> class T(object):                                                        
...     def __init__(self, id, children):                                   
...         self.id = id                                                    
...         self.children = children or []                                  
...                                                                         
...     def __repr__(self):                                                 
...         return u"T(children={0}, id={1})".format(self.children, self.id)
...                                                                         
>>> l0 = [T(id=1, children=[]), T(id=2, children=[]), T(id=3, children=[]), 
... [T(id=40, children=[]), T(id=41, children=[]), T(id=42, children=[]),   
... T(id=43, children=[]), T(id=44, children=[]), T(id=45, children=[]),    
... [T(id=50, children=[]), T(id=51, children=[]), T(id=52, children=[]),   
... T(id=54, children=[]), [T(id=60, children=[]), T(id=61, children=[]),   
... T(id=62, children=[]), T(id=63, children=[])]]], T(id=8, children=[]),  
... T(id=9, children=[])]                                                   
>>> l1 = magic(l0)                                              
>>> l1[0]                                                                   
T(children=[], id=1)                                                        
>>> l1[1]                                                                   
T(children=[], id=2)                                                        
>>> l1[3]                                                                   
T(children=[], id=8)                                                        
>>> l1[4]                                                                   
T(children=[], id=9)                                                        
>>> l1[5]                                                                   
Traceback (most recent call last):                                          
    ...                                                                     
IndexError: list index out of range                                         
>>> l1[2].children[5].children[3]                                           
T(children=[T(children=[], id=60), T(children=[], id=61), T(children=[], id=62), T(children=[], id=63)], id=54)
>>> l0 = [T(id=1, children=[]), T(id=2, children=[]), T(id=3, children=[]), 
... [T(id=40, children=[]), T(id=41, children=[]), T(id=42, children=[]),   
... T(id=43, children=[]), T(id=44, children=[]), T(id=45, children=[]),    
... [T(id=50, children=[]), T(id=51, children=[]), T(id=52, children=[]),   
... T(id=54, children=[]), [T(id=60, children=[]), T(id=61, children=[]),   
... T(id=62, children=[]), T(id=63, children=[]), [T(id=70, children=[])],  
... T(id=64, children=[])]]], T(id=8, children=[]), T(id=9, children=[])]   
>>> l1 = magic(l0)                                              
>>> l1[2].children[5].children[0].id                                        
50                                                                          
>>> len(l1[2].children[5].children[3].children)                             
5                                                                           
>>> l1[2].children[5].children[3].children[3].children                      
[T(children=[], id=70)]                                                     
>>> l1[2].children[5].children[3].children[4].id==64                        
True                                                                  

使用@rob-wouters的替代方案,它也通过了相同的测试,因此对于我尝试的测试用例,两者都可以正常工作。我会保留Rik的方法,因为我认为独立的函数在需要这种行为的情况下更加方便。


最后一个元素是否总是一个列表? - 2rs2ts
2
抛开问题不谈,如果有一个“本月最糟糕变量名”的奖项,我会提名“l0”。它几乎无法读取,因为它同时类似于数字“10”、感叹词“lo”和单词“io”。 - jsbueno
是的,对不起,我耗尽了最后一丝思维去问问题了! :) - MayVimmer Imeil
@agarrett 实际上不是这样的。我应该添加一个更完整的例子。 - MayVimmer Imeil
1
@MayVimmerImeil 我已经开始处理你的问题,并发现了几个特殊情况,每种情况下该怎么做并不清楚。例如(简化符号,但你可以理解):[[1] [2]][[1] 2][1 [2] [3]][1 [2] [3] 4][1 [2 [3]]] - Óscar López
这些列表有多深的限制吗? - Joel Cornett
2个回答

3
这是我会这样做的方法:
class T(object):
    def __init__(self, id, children):
         self.id = id 
         self.children = children or []

    def add_children(self, children):
        for child in children:
            if isinstance(child, list):
                self.children[-1].add_children(child)
            else:
                self.children.append(child)

    def __repr__(self):
         return u"T(id={0}, children={1})".format(self.id, self.children) 


l0 = [T(id=1, children=[]),
      T(id=2, children=[]), [T(id=3, children=[]), T(id=4, children=[]),
                            [T(id=5, children=[])]]]

root = T(id=0, children=[])
root.add_children(l0)
print(root.children)

如果你真的想要一个独立的方法,而不是有两个处理相同情况的函数,可以使用以下方法:

def add_children(node, children):
    for child in children:
        if hasattr(child, '__iter__'):
            add_children(node.children[-1], child)
        else:
            node.children.append(child)

def create_tree(lst):
    root = T(id=0, children=[])
    add_children(root, lst)
    return root.children

print(create_tree(l0))

与拥有两个几乎相同的函数相比,这种方法更加优雅,因为它避免了大量重复的代码。我改变了我的isinstance检查,转而检查__iter__,这样可以更灵活地存储孩子列表。


谢谢Rob,我会测试一下并告诉大家结果。 - MayVimmer Imeil
@MayVimmerImeil,阅读了您编辑后的答案后,我建议使用一个独立的方法,我认为这样更加简洁易读。请告诉我您的想法。 - Rob Wouters

1
我想到了这个:
def append_children(parent, iterable):
    last = None
    for i in iterable:
        if hasattr(i, '__iter__'):
            append_children(last, i)
        else:
            parent.children.append(i)
            last = i

def magic(lst):
    result = []
    for i in lst:
        if hasattr(i, '__iter__'):
            append_children(result[-1], i)
        else:
            result.append(i)
    return result

例子:

>>> l_in = [T(id=1, children=[]), T(id=2, children=[]), [T(id=3, children=[]), 
...         T(id=4, children=[]), [T(id=5, children=[])]]]
>>> l_expected = [T(id=1, children=[]),
...               T(id=2, children=[T(id=3, children=[]), 
...                                 T(id=4, children=[T(id=5, children=[])])])]
>>> l_ouput = magic(l_in)
>>> repr(l_output) == repr(l_expected)
True

2
在这种情况下,您可以删除您的答案,在删除时进行编辑,并在您满意您的答案后取消删除。 - David Alber
谢谢 Rik,我会测试一下并告诉你结果如何。 - MayVimmer Imeil
@MayVimmerImeil:看起来你可能有很多子/特殊情况,所以我有点好奇这段代码是否能够处理它们。 - Rik Poggi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接