为什么扁平化列表中有空白项

3

我正在尝试以下递归函数来展开可能包含子列表项的句子列表:

def myflatten(slist, outlist=[]):
    for sl in slist:
        if type(sl) == list:    
            outlist.append(myflatten(sl, outlist))
        else: 
            outlist.append(sl)
    return outlist

print("myflatten list=", myflatten([1,[5,6,7],3,4,[7,8,9]]))

输出:

myflatten list= [1, 5, 6, 7, [...], 3, 4, 7, 8, 9, [...]]

为什么每个子列表都会显示[...],如何避免这种情况?感谢您的帮助。

2
[...] 表示列表包含它自己。 - turbulencetoo
我认为问题在于您正在更新outlistdefault引用。例如,如果您调用myflatten([1]),然后调用myflatten([2]),您将得到[1, 2] - Matias Cicero
1
这不是你问题的原因,但要注意在函数定义中的默认可变陷阱。即使你让你的代码工作了,如果你调用它超过一次,它可能会产生令人惊讶的结果。 - Kevin
@turbulencetoo 很有趣!以前没见过,每天都能学到新东西! - Joe Iddon
@Kevin 实际上,我认为这就是问题的原因。这是一个递归调用。 - Matias Cicero
@MatiasCicero,即使他删除了默认参数并改为要求显式引用,问题仍会发生。示例 - Kevin
4个回答

2
现有的答案对为什么会出现 [...] 自我引用提供了很好的解释,但是它们对代码的建议修改并没有解决等待咬你的 默认可变参数陷阱
这里有一个不需要 outlist 参数的解决方案:
def myflatten(slist):
    outlist = []
    for sl in slist:
        if isinstance(sl, list):    
            outlist.extend(myflatten(sl))
        else: 
            outlist.append(sl)
    return outlist

print("myflatten list=", myflatten([1,[5,6,7],3,4,[7,8,9]]))

是的,它完美地工作了。extend 是一个有用的函数。 - rnso
在Scheme/Racket/Lisp衍生语言中,将一个空的“累加器”作为参数是很常见的(例如 https://dev59.com/04nca4cB1Zd3GeqP9leU)。 - rnso

1
问题在于myflatten返回一个列表。因此,当您将myflatten的结果附加到outlist时,它会附加一个列表。尝试将outlist设置为myflatten的结果:
def myflatten(slist, outlist=[]):
    for sl in slist:
        if type(sl) == list:    
            outlist = myflatten(sl, outlist) #HERE IS THE CHANGE
        else: 
            outlist.append(sl)
    return outlist

print("myflatten list=", myflatten([1,[5,6,7],3,4,[7,8,9]]))

但是我发现随后的调用会继续添加到之前对该函数创建的outlist中。如何纠正这个问题? - rnso
@rnso,你能给我举个例子来说明你的意思吗? - NendoTaka
尝试在你的函数上运行print(myflatten([1])); print(myflatten([2]))。你将会得到第二个命令的输出为[1,2] - rnso
有趣,学到了新东西。Kevin的答案更好,因为它不使用outlist作为参数。我可以更新这个答案,但既然他的答案有效,除非你想让我这样做,否则我不会更新。 - NendoTaka
请参考此链接:https://dev59.com/9nNA5IYBdhLWcg3wAItP - rnso
是的,这就是我在Kevin的链接中看到的,这意味着使用outlist作为参数是一个不好的主意。有一些其他的答案因此而工作得更好。 - NendoTaka

1
你已经创建了一个无限列表。列表中的[...]条目指向列表本身。有关更多详细信息,请参见this answer
这是因为当您进行递归调用时,会将outlist对象本身传递进去,然后返回并将其附加到自身。这是一个微妙的问题!但很容易解决;您需要使用=进行递归调用,而不是append。或者使用extend,并传入一个空列表。
def myflatten(slist):
    outlist = []
    for sl in slist:
        if type(sl) == list:    
            outlist.extend(myflatten(sl))
        else: 
            outlist.append(sl)
    return outlist

你甚至可以删除一个参数,这很好。


1

另一种不使用 list.extend 的选项。我会稍微调整你的代码,尽量避免使用默认参数:

def myflatten(slist):
    flat = []
    if isinstance(slist, list):
        for item in slist:
            flat += myflatten(item)
    else:
        flat.append(slist)
    return flat

@rnso 确实。我不知道效率是否有任何差别,但无论如何都不应该太大。 - Mr. Xcoder

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