繁体   English   中英

python 列表 scope 在 class

[英]python list scope in class

我遇到了这段代码的一些意外行为:

from pprint import pprint

class a (object):
    x = ['1']

class b (a):
    x = a.x
    x.append('2')

class c (a):
    x = a.x
    x.append('3')

class d (a):
    x = a.x
    x = 'nothing'

if __name__ == '__main__':
    pprint(a.x)
    pprint(b.x)
    pprint(c.x)
    pprint(d.x)

我收到 output:

['1', '2', '3']
['1', '2', '3']
['1', '2', '3']
'nothing'

但我希望收到:

['1']
['1', '2']
['1', '3']
'nothing'

我不明白的是:

  1. 为什么将 class b 中的列表也附加到 append 到 class a 中的列表中?
  2. 为什么将 class c append 中的列表附加到 b 和 a?
  3. 为什么不将该变量重新分配给 class d 中的字符串对其他 3 个类没有影响?

你的线

class b (a):
    x = a.x

ax创建另一个“名称”,即x (在该范围内),但它们是相同的 object。 如果您将 append 附加到x ,您还附加到ax - 它附加到相同的 object。

你做一些不同的事情的唯一地方是在

x = 'nothing'

您现在将x绑定到不同的 object,一个字符串。


如果您将代码更改为

class b (a):
    x = a.x.copy()

你会得到不同的行为:这表示x现在是ax列表副本的“名称”。

1. 2.在执行x = ax时,您只是将x指向唯一存在的列表,即来自a的列表,因此在对x进行操作时,这反映在ax上,因为这是同一个列表,而不是副本。 这对于class bclass c都是正确的。 做一个副本

x = list(a.x)

3.执行x='nothing'时,您将一个字符串分配给x ,该字符串不再指向列表,仅此而已

当您以这种方式定义它时。 变量 x 使用父 class 初始化,每个继承 class 的子都引用该变量(不是副本)。 它成为 class 名称下的全局变量。

要实现您期望的 output:

from pprint import pprint

class a (object):
    def __init__(self):
        self.x = ['1']

class b (a):
    def __init__(self):
        super().__init__()
        self.x.append('2')

class c (a):
    def __init__(self):
        super().__init__()
        self.x.append('3')

class d (a):
    def __init__(self):
        super().__init__()
        self.x = 'nothing'

if __name__ == '__main__':
    pprint(a().x)
    pprint(b().x)
    pprint(c().x)
    pprint(d().x)

对于新的 Python 开发人员来说,这可能是最难理解的事情之一,当他们最终理解时,这将成为一个真正的顿悟。 您所拥有的与以下内容没有什么不同:

>>> x = [1]; print(x)
[1]

>>> y = x; print(x); print(y)
[1]
[1]

>>> y.append(2); print(x); print(y)
[1, 2]
[1, 2]

>>> z = x; z = 'nothing'; print(x); print(y); print(z)
[1, 2]
[1, 2]
nothing

这样做的原因是因为在 Python 中,一切都是 object 并且变量只是与对象的绑定

所以x = [1]创建了object [1]并将x变量绑定到它。

然后,当您执行y = x时,不会创建新的 object,它只是将y绑定到已经存在的 object。 这意味着xy现在都绑定到同一个object 因此,当您执行修改 object 的操作时,例如x.append()xy都会受到影响。

z = x; z = 'nothing'不会发生这种情况。 z = x; z = 'nothing'因为第二步是创建一个的 object 并将z绑定到它,因此x/yz现在绑定到不同的东西。

它也不会发生在以下任何一种情况下:

z = x[:]
z = [item for item in x]

因为这两个还创建了一个新的 object (原始(1)的副本),将xz分开。

通过检查每个变量的 ID(可能是您应该使用id的唯一原因)可以看到效果,为了便于阅读,删除了公共前缀:

>>> print(id(x)); print(id(y)); print(id(z))
49864
49864
53808

可以看到xy同一个object, z是不同的。


(1)请记住,如果您的数据结构足够复杂,您可能需要进行拷贝,例如(添加注释):

>>> x = [[1,2],[3,4]]; y = x; print(id(x)); print(id(y))
773190920  # same object
773190920

>>> y = x[:]; print(id(x)); print(id(y))
773190920  # different objects
832649864

>>> print(id(x[0])); print(id(y[0]))
773088840  # but the sub-objects are still identical.
773088840

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM