为什么 python 中 class 属性的赋值行为类似于实例变量的赋值?
Why assignment of the class attributes in python behaves like assignment of instace variables?
在 python 中谈论 class 属性时的常见示例如下:
Python 2.7.6 (default, Sep 9 2014, 15:04:36)
>>> class B(object):
... cv = []
...
>>> b1 = B()
>>> b2 = B()
>>> b1.cv, b2.cv, B.cv
([], [], [])
>>> b1.cv.append(1)
>>> b1.cv, b2.cv, B.cv
([1], [1], [1])
>>> b2.cv.append(2)
>>> b1.cv, b2.cv, B.cv
([1, 2], [1, 2], [1, 2])
>>> B.cv.append(3)
>>> b1.cv, b2.cv, B.cv
([1, 2, 3], [1, 2, 3], [1, 2, 3])
这表明 class 属性在 class 及其所有实例之间共享。
但是当我们重新分配 class 属性的值时会发生这种情况,即没有绑定到 class 属性的初始对象的突变:
>>> class A(object):
... cv = 0
...
>>> a1 = A()
>>> a2 = A()
>>> a1.cv, a2.cv, A.cv
(0, 0, 0)
>>> a1.cv = 1
>>> a1.cv, a2.cv, A.cv
(1, 0, 0)
>>> a2.cv = 2
>>> a1.cv, a2.cv, A.cv
(1, 2, 0)
>>> A.cv = 3
>>> a1.cv, a2.cv, A.cv
(1, 2, 3)
在这里我们可以看到,每次这个 class 属性都存储了它的唯一值,并且在实例和 class 命名空间应用的下一个分配中它不会被覆盖。
为什么会这样?
我不明白这种逻辑可能会导致 "immutable" (A) 和 "mutable" (B) 的 'not relevant' 行为。
这让我想到 "no any sense of using class variables" 因为他们可能容易出错...
我希望在这条隧道里看不到光明的是我...
在第一个示例中,您改变了列表。宇宙中只有一个列表实例,B.__dict__['cv']
。在第二个示例中,您分配值。当您执行此操作时,它们会在每个特定实例 a(1|2|3)
中进行分配,因为这就是属性设置在 Python 中的工作方式(它会保存到您尝试更改属性的任何内容的 __dict__
的)。您必须修改 A.cv
才能修改所有内容,并且在 a(1|2|3)
中所做的任何更改都将覆盖所做的更改。
(Python 尝试使用 a(1|2|3).__dict__
然后退回到 A.__dict__
。)
最后一个例子解释了 Chris Warrick 的回答
>>> A.cv = 0
>>> a1, a2 = A(), A()
>>> A.cv, a1.cv, a2.cv
(0, 0, 0)
>>> A.cv = 1
>>> A.cv, a1.cv, a2.cv
(1, 1, 1)
>>> a1.cv = 2 # Here the new instance attribute is created for a1,
# and so it will hide the class attribute with the same name,
# once getting the value from instance namespace
>>> A.cv, a1.cv, a2.cv
(1, 2, 1)
>>> A.cv = 3
>>> A.cv, a1.cv, a2.cv
(3, 2, 3)
如果您不打算通过实例使用它们,则可以有效地使用 class 属性。比如我喜欢在class属性中管理一组相同class的对象。如果您听说过 Pygame,那就是我最常使用此技术的地方。
class Alien:
sprites = []
def __init__(self, x, y):
self.surf = pygame.image.load('Alien.png')
self.rect = self.surf.get_rect()
self.rect.topleft = (x, y)
Alien.sprites.append(self)
@staticmethod
def draw_sprites(screen):
for sprite in Alien.sprites:
screen.blit(sprite.surf, sprite.rect)
您知道使用 class 方法和属性可以如此轻松地进行对象管理吗?
在 python 中谈论 class 属性时的常见示例如下:
Python 2.7.6 (default, Sep 9 2014, 15:04:36)
>>> class B(object):
... cv = []
...
>>> b1 = B()
>>> b2 = B()
>>> b1.cv, b2.cv, B.cv
([], [], [])
>>> b1.cv.append(1)
>>> b1.cv, b2.cv, B.cv
([1], [1], [1])
>>> b2.cv.append(2)
>>> b1.cv, b2.cv, B.cv
([1, 2], [1, 2], [1, 2])
>>> B.cv.append(3)
>>> b1.cv, b2.cv, B.cv
([1, 2, 3], [1, 2, 3], [1, 2, 3])
这表明 class 属性在 class 及其所有实例之间共享。
但是当我们重新分配 class 属性的值时会发生这种情况,即没有绑定到 class 属性的初始对象的突变:
>>> class A(object):
... cv = 0
...
>>> a1 = A()
>>> a2 = A()
>>> a1.cv, a2.cv, A.cv
(0, 0, 0)
>>> a1.cv = 1
>>> a1.cv, a2.cv, A.cv
(1, 0, 0)
>>> a2.cv = 2
>>> a1.cv, a2.cv, A.cv
(1, 2, 0)
>>> A.cv = 3
>>> a1.cv, a2.cv, A.cv
(1, 2, 3)
在这里我们可以看到,每次这个 class 属性都存储了它的唯一值,并且在实例和 class 命名空间应用的下一个分配中它不会被覆盖。
为什么会这样?
我不明白这种逻辑可能会导致 "immutable" (A) 和 "mutable" (B) 的 'not relevant' 行为。 这让我想到 "no any sense of using class variables" 因为他们可能容易出错...
我希望在这条隧道里看不到光明的是我...
在第一个示例中,您改变了列表。宇宙中只有一个列表实例,B.__dict__['cv']
。在第二个示例中,您分配值。当您执行此操作时,它们会在每个特定实例 a(1|2|3)
中进行分配,因为这就是属性设置在 Python 中的工作方式(它会保存到您尝试更改属性的任何内容的 __dict__
的)。您必须修改 A.cv
才能修改所有内容,并且在 a(1|2|3)
中所做的任何更改都将覆盖所做的更改。
(Python 尝试使用 a(1|2|3).__dict__
然后退回到 A.__dict__
。)
最后一个例子解释了 Chris Warrick 的回答
>>> A.cv = 0
>>> a1, a2 = A(), A()
>>> A.cv, a1.cv, a2.cv
(0, 0, 0)
>>> A.cv = 1
>>> A.cv, a1.cv, a2.cv
(1, 1, 1)
>>> a1.cv = 2 # Here the new instance attribute is created for a1,
# and so it will hide the class attribute with the same name,
# once getting the value from instance namespace
>>> A.cv, a1.cv, a2.cv
(1, 2, 1)
>>> A.cv = 3
>>> A.cv, a1.cv, a2.cv
(3, 2, 3)
如果您不打算通过实例使用它们,则可以有效地使用 class 属性。比如我喜欢在class属性中管理一组相同class的对象。如果您听说过 Pygame,那就是我最常使用此技术的地方。
class Alien:
sprites = []
def __init__(self, x, y):
self.surf = pygame.image.load('Alien.png')
self.rect = self.surf.get_rect()
self.rect.topleft = (x, y)
Alien.sprites.append(self)
@staticmethod
def draw_sprites(screen):
for sprite in Alien.sprites:
screen.blit(sprite.surf, sprite.rect)
您知道使用 class 方法和属性可以如此轻松地进行对象管理吗?