在Python中混淆列表:它是什么?

时间:2008-12-29 02:46:31

标签: python recursion list recursive-datastructures

所以我在Python中编写了一个简单的二叉树,并且遇到了[...]

我不相信这与Ellipsis对象有关,它似乎与无限循环有关(由于Python的浅拷贝?)。这个无限循环的来源以及为什么它在被访问时扩展时不会被扩展是我完全迷失的东西

>>> a
[[[[[], [], 8, 3], [[], [], 3, 2], 6, 3], [], 1, 4], [[], [], -4, 2], 0, 0]
>>> Keys(a)#With a+b
[0, 1, 6, 8, 3, -4]
>>> Keys(a)#With [a,b]
[8, [...], [...], 3, [...], [...], 6, [...], [...], 1, [...], [...], -4, [...], [...], 0, [...], [...]]
>>> Keys(a)[1]#??
[8, [...], [...], 3, [...], [...], 6, [...], [...], 1, [...], [...], -4, [...], [...], 0, [...], [...], 8, [...], [...], 3, [...], [...], 6, [...], [...], 1, [...], [...], -4, [...], [...], 0, [...], [...]]

>>> a [[[[[], [], 8, 3], [[], [], 3, 2], 6, 3], [], 1, 4], [[], [], -4, 2], 0, 0] >>> Keys(a)#With a+b [0, 1, 6, 8, 3, -4] >>> Keys(a)#With [a,b] [8, [...], [...], 3, [...], [...], 6, [...], [...], 1, [...], [...], -4, [...], [...], 0, [...], [...]] >>> Keys(a)[1]#?? [8, [...], [...], 3, [...], [...], 6, [...], [...], 1, [...], [...], -4, [...], [...], 0, [...], [...], 8, [...], [...], 3, [...], [...], 6, [...], [...], 1, [...], [...], -4, [...], [...], 0, [...], [...]]

使用+ b

的版本

版本使用[a,b]

def Keys(x,y=[]):
    if len(x):y+=[x[2]]+Keys(x[0],y)+Keys(x[1],y)#Though it seems I was using y=y[:]+, this actually outputs an ugly mess
    return y

究竟是什么[...]?

9 个答案:

答案 0 :(得分:25)

如果您的圆形结构中有一个指向自身的列表,也会出现。像这样:

>>> a = [1,2]
>>> a.append(a)
>>> a
[1, 2, [...]]
>>> 

由于python无法打印出结构(它将是一个无限循环),因此它使用省略号来表明结构中存在递归。


我不太确定问题是发生了什么或如何解决,但我会尝试纠正上述功能。

在它们中,您首先进行两次递归调用,将数据添加到列表y,然后AGAIN将返回的数据追加到y。这意味着结果中将多次出现相同的数据。

要么只收集所有数据而不添加任何y,请使用类似

的内容
return [x[2]]+keys(x[0])+keys(x[1])

或只是在电话中添加附加内容,例如

y += [x[2]]
keys(x[0], y) #Add left children to y...
keys(x[1], y) #Add right children to y...
return y

(当然,这两个片段都需要处理空列表等)

@Abgan还注意到你真的不想在初始化程序中使用y=[]

答案 1 :(得分:6)

我相信,你的'树'包含自己,因此它包含周期。

试试这段代码:

   a = [1,2,3,4]
   print a
   a.append(a)
   print a

第一个打印输出:

  [1,2,3,4]

而第二个:  

  [1,2,3,4, [...]]
 

原因是使用  

 def Keys(x,y=[]):
 
这是错误和邪恶的。 List是一个可变对象,当用作默认参数时,它在函数调用之间保留。 所以每个y + =“anything”操作都会添加到相同的列表中(在所有函数调用中,因为函数是递归的...)


有关作为函数默认值传递的可变对象的更多详细信息,请参阅EffbotDevshed

答案 2 :(得分:5)

我不明白你上面的代码,但我认为是Python解释器跳过无限的数据结构。例如:

>>> a = [0, 1]
>>> a[0] = a
>>> a
[[...], 1]

看起来你的树形结构正在变成循环。

关于切片对象的答案是不重要的。

答案 3 :(得分:4)

  

我不相信这与Ellipsis对象有关,它似乎与无限循环有关(由于Python的浅拷贝?)。这个无限循环的来源以及为什么它在访问时不会扩展,这是我完全迷失的东西,但是

请查看以下代码:

>>> a = [0]
>>> a.append(a)
>>> print a
[0, [...]]

Python应如何打印?它是一个包含零的列表和对自身的引用。因此,它是一个包含零和列表引用的列表

[0, [...]]

又包含零和对列表的引用

[0, [0, [...]]]

又包含零和对列表的引用, 等等,递归地:

[0, [0, [0, [...]]]]
[0, [0, [0, [0, [...]]]]]
[0, [0, [0, [0, [0, [...]]]]]]
...

递归数据结构本身没有任何问题。唯一的问题是它不能显示,因为这意味着无限递归。因此,Python在第一个递归步骤停止并处理无限问题,只打印省略号,如前面的答案中所指出的那样。

答案 4 :(得分:4)

如果您使用的是PrettyPrinter,那么输出就会自我解释

>>> l = [1,2,3,4]
>>> l[0]=l
>>> l
[[...], 2, 3, 4]
>>> pp = pprint.PrettyPrinter(indent = 4)
>>> pp.pprint(l)
[<Recursion on list with id=70327632>, 2, 3, 4]
>>> id(l)
70327632

换句话说就像

enter image description here

答案 5 :(得分:2)

编辑:如上所述,这不是Ellipsis对象,而是循环列表的结果。我在这里跳了枪。如果您在某些实际代码中找到省略号而不是输出,那么了解省略号对象是一个很好的后台知识。


Python中的Ellipsis对象用于扩展切片表示法。它不在当前的Python核心库中使用,但可供开发人员在自己的库中定义。例如,NumPy(或SciPy)将其用作数组对象的一部分。您需要查看tree()的文档,以确切了解Ellipsis在此对象中的行为。

来自Python documentation

  

3.11.8省略号对象

     

此对象由扩展切片使用   符号(参见Python参考资料)   手册)。它没有特别的支持   操作。只有一个   省略号对象,名为省略号(a   内置名称)。

     

它写成省略号。

答案 6 :(得分:1)

好的,所以要点:

  1. 您正在创建无限数据 结构:

    def Keys(x,y=[])
    将使用相同的'y' 每次通话。这是不对的。

  2. 然而,print语句非常聪明,不会打印无限数据,而是用[...]标记自引用(称为省略号

  3. Python将允许您正确地处理此类结构,因此您可以编写
    a.keys()[1][1][1]
    等等。你为什么不呢?
  4. y = y[:]语句只是复制列表y。可以使用y = list(y)
  5. 更好地完成

    尝试使用以下代码:

    def Keys(x,y=None):
        if y is None:
            y = []
        if len(x):
            y += [x[2], Keys(x[0],y), Keys(x[1],y)]
        return y
    

    但我仍然觉得它可以咬你。您仍然在一个表达式中的三个位置使用相同的变量y(我的意思是相同的对象):

     y += [x[2], Keys(x[0], y), Keys(x[1], y)] 

    这是你真正想要实现的吗? 或许你应该尝试:

    def mKeys(x,y=None):
        if y is None:
            y = []
        if len(x):
           z = [x[2], mKeys(x[0], y), mKeys(x[1],y)]
           return z
       return []
    

答案 7 :(得分:1)

对于两个版本的功能键之间的区别,请注意以下差异:

y+=[x[2]]+Keys(x[0],y)+Keys(x[1],y)

此语句中的右侧值是一个包含x [2]的列表,加上元素的元素(x [0],y)和键的元素(x [1],y)

y+=[x[2],Keys(x[0],y),Keys(x[1],y)]

此语句中的右侧值是一个包含x [2]的列表,加上LIST键(x [2],y)和LIST键(x [1],y)。

因此使用[a,b]的版本将导致y将其自身包含为其元素。

其他一些说明:

  1. 因为在python中,默认值对象是在定义函数时创建的,所以第一个版本不会像示例所示那样工作。它将包含一些键的多个副本。简而言之,很难解释,但是你可以通过在每次调用Keys时打印x,y的值来获得一些想法。

    通过使用python 2.5.2在我的机器上运行该功能可以确认这一点。

  2. 另外因为默认值在函数定义时只定义了一次,所以即使该函数第一次正常工作,在使用不同的a调用时它也不起作用,因为第一个二叉树中的键将是留在y。

    你可以通过两次调用Keys(a)或在两个不同的列表上调用它来看到这一点。

  3. 此问题不需要第二个参数。功能可以是这样的:

    def键(a):     如果a = []:         返回[]     其他:         返回[a [2]] +键(a [0])+键(a [1])

    定义递归函数基本上包含两部分,解决子问题并组合结果。在你的代码中,组合结果部分重复两次:一个是在y中累加它们,一个是将列表一起添加。

答案 8 :(得分:1)

问题是因为其中一个列表元素引用了列表本身。因此,如果尝试打印所有元素,那么它将永远不会结束。

<强>插图:

x = range(3)
x.append(x)
x[3][3][3][3][3][0] = 5
print x

输出:

[5, 1, 2, [...]]

x[3]指的是x本身。同样适用于x[3][3]

这可以更好地可视化 here