繁体   English   中英

Python 可变类变量与不可变类变量

[英]Python mutable class variable vs immutable class variable

运行下面的示例代码:

class S:
    i = 0
    a = []
    def __init__(self):
        self.i += 1
        self.a.append(1)

s1 = S()
print((s1.i, s1.a))

s2 = S()
print((s2.i, s2.a))

输出将是:

(1, [1])
(1, [1, 1])

我的问题是为什么 s2 的 int Si 重置为 0 但列表 Sa 没有重置为空? 我认为这与不可变 int 与可变列表有关,但有人可以帮助表达在两个 init 调用期间两个类变量发生了什么的更多细节吗? 谢谢!

因此,当您调用s1.is1.a时,您正在更改实例属性。 要更改类属性,请尝试以下操作:

S.i += 1
S.a.append(1)

在您的构造函数中,您初始化self.aself.i 这将创建属于类的每个实例的实例属性。

在构造函数之外声明的ai是类属性,并且由所有实例共享。

无论使用哪个属性, s1.aSa都会更新的原因是列表是可变的,并且实例和类变量都是对同一个列表的引用。

self.i += 1

相当于

self.i = self.i + 1

当实例变量不存在时,在类上查找值,所以在这种场景下,相当于

self.i = S.i + 1

定义self.i后,任何进一步的值查找都在实例变量上,而不是在类变量上。 所以在这条线之后,你有Si = 0s1.i = 1 由于Si未修改, s2.i也变为1

另一方面,

self.a.append(1)

不会创建新的实例变量,而是将元素附加到现有的类变量。

这段特定代码的编写方式抽象了 Python 在幕后所做的一些事情,所以让我们来看看它。

当您定义类并像在代码开头那样定义任何函数之外的变量时,它会创建类属性 这些在您的类的所有实例之间共享(在您的情况下, s1s2都共享对您的i对象和您a对象的相同引用)。

当您初始化类时,您正在调用__init__函数,该函数在您的代码中首先调用self.i += 1 ,我认为这是大多数混乱的来源。 在 Python 中,整数是不可变的,因此它们不能被覆盖。 通过调用+= ,您将删除对旧i变量的引用并创建一个引用内存中不同位置的新变量。 但是因为你现在在你的类中的一个函数中,它被定义为一个实例属性 实例属性不会在类的不同实例之间共享。

但是,列表可变的。 因此,当您将1附加到您的列表时,您并没有创建一个新的实例变量,因此它保持对类属性的相同引用,因此当您第二次初始化您的类时,它会将其添加到已经具有的类属性中创建第一个实例时填充一次。

class S:
    i = 0
    a = []
    def __init__(self):
        self.i += 1
        self.a.append(1)

a = []定义的列表是一个类属性。 它在定义类时实例化,并保持相同的列表对象。 此类的任何实例都将引用一个列表。

如果您想为每个新实例创建一个空列表,请将列表定义移动到__init__方法中:

class S:
    i = 0
    def __init__(self):
        self.a = []
        self.i += 1
        self.a.append(1)

结果:

>>> s1 = S()
>>> print((s1.i, s1.a))
(1, [1])
>>> 
>>> s2 = S()
>>> print((s2.i, s2.a))
(1, [1])

暂无
暂无

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

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