list.clear() 与 list = [] 有何不同?



函数foo的预期目标是将作为参数提供的数字添加到列表中,如果为 0,则重置列表。首先我写了这个程序:

def foo(n, bar = []):
if n == 0:
bar = []
print("list empty")
else:
bar.append(n)
for y in bar:
print(y, end=', ')
print()
foo(5)
foo(3)
foo(0)
foo(6)

输出:

5, 
5, 3, 
list empty
5, 3, 6, 

但看起来bar = []被忽略了。然后我用bar.clear()更改了bar = [],它的工作原理就像我想的那样:

def foo(n, bar = []):
if n == 0:
bar.clear()
print("list empty")
else:
bar.append(n)
for y in bar:
print(y, end=', ')
print()
foo(5)
foo(3)
foo(0)
foo(6)

输出:

5, 
5, 3, 
list empty
6,

我不明白为什么bar.clear()的工作方式与bar = []不同,因为clear()应该

从集合中删除所有元素。

所以bar = []也做了同样的事情。

编辑:我不认为我的问题是"最不惊讶"和可变默认参数的重复,我知道

默认值仅计算一次。

(来自官方教程(但我问的是,为什么bar = []不编辑(在本例中为清除(列表,而附加和清除可以?

bar = []

重新绑定bar;它创建一个全新的空list,并将名称bar绑定到它上面。以前是什么bar并不重要;它本来可以是一个int,一个tuple,或者dict,现在它已经无关紧要了,因为它现在绑定到一个全新的list。旧绑定不再存在于当前方法调用中,但原始list作为默认参数的值仍然存在foo(注意:由于这种混淆,可变默认值被认为是令人惊讶和丑陋的(。

bar.clear()现有list上调用一个方法,它告诉list清除自己(并且对bar所指的内容没有影响;它最好是具有clear方法的东西,但除此之外,它实际上并不需要它是一个list(。由于它与您开始使用的bar相同(引用函数默认值中缓存的相同可变默认值(,这会影响您将来的调用(因为bar继续引用您提供的list作为可变默认值(。

你真正应该理解的是变量在Python中是如何工作的。

变量始终只是对实际内容的引用。变量本身不存储任何内容,它只指向未命名的内容。

现在,当你调用foo()时,你所拥有的是内存中的一个列表,局部bar变量指向它。如果您调用bar.clear(),该列表实际上已被清除,这就是您想要的。但是,如果您说bar = []您只需将局部bar变量绑定到一个新的空列表,另一个列表(已经包含 5 和 3(保持不变,下次调用foo()时将使用它。

在代码中,bar是对内存中某个列表的引用。 使用bar = []时,变量bar与新的(空(列表相关联。使用bar.clear()时,可以修改当前列表。

一般来说,这意味着 Python 中的列表是通过引用而不是按值传递的。您可以在此处阅读有关它的更多信息。

当你做一个list.clear()时,列表会被清除:-( 当您执行list = []时,该列表将被一个新的空列表覆盖。

不同之处在于,如果某些内容在 if 被覆盖之前引用了该列表,它仍将引用您认为现在已被清除的值。

这是引入极难调试错误的绝佳方法。

假设您有一个包含一些值的列表,并将其分配给名为 foo 的变量。

foo = [0, 1]

此变量现在指向内存中的此列表。

假设您随后创建了一个名为 bar 的新变量并分配给 foo。

bar = foo

这两个变量现在指向内存中的同一列表对象。如果你然后改变foo并让它相等[],酒吧会发生什么?

foo = []
bar == [0, 1] # True

现在让我们用 foo.clear(( 做同样的事情。

foo.clear()
bar == [] # True

这是因为在列表上使用 clear 方法会清除该列表对象,这会影响引用此对象的所有内容,而将变量分配给空列表只会更改该变量的内容,而不会更改原始列表。

它具有所需的属性:

def foo(n, bar = []):
if n == 0:
bar[:] = []
print("list empty")
else:
bar.append(n)
for y in bar:
print(y, end=', ')
print()

最新更新