【问题标题】:Why assignment of the class attributes in python behaves like assignment of instace variables?为什么在 python 中分配类属性的行为类似于分配实例变量?
【发布时间】:2015-03-07 19:18:13
【问题描述】:

在python中谈论类属性时的常见示例如下:

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 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)

在这里我们可以看到,每次这个类属性存储它的唯一值,并且在实例和类命名空间应用的下一个赋值中它不会被覆盖。

为什么会这样?

我无法理解这种逻辑可能会导致“不可变”(A)和“可变”(B)情况的“不相关”行为。 这让我想到“没有任何使用类变量的意义”,因为它们可能容易出错......

我希望在这条隧道里看不到光的人是我……

【问题讨论】:

  • 如果您不打算通过实例使用类属性,则可以有效地使用它们。例如,我喜欢在类属性中管理同一类的一组对象。如果你听说过 Pygame,那我就是最常使用这种技术的地方。
  • 是的,当我看到这个时,我想,'a1.cv!? a2.cv!?这些是静态类变量!我发现 Python 允许你混合搭配它的使用方式,这让我感到非常不安。我会避免通过实例成员访问静态类,并且永远不知道有问题。至于为什么 Python 允许您执行上述代码 - 我想这在下面的答案中有所解释,但这似乎是陷入麻烦的好方法;p
  • @MalikBrahimi,如果你提供一些这种技术的小例子,那就太好了,只是为了了解可以使用它的真实案例......并放置一些指向 gist 或 pastebin 的链接,或其他...谢谢!
  • @yashaka 请稍等,我会发一个帖子。

标签: python class attributes class-attributes


【解决方案1】:

在第一个示例中,您更改了列表。 Universe 中只有一个列表实例,B.__dict__['cv']。在第二个示例中,您分配值。当您执行此操作时,它们会在每个特定实例 a(1|2|3) 中进行分配,因为这就是 Python 中属性设置的工作方式(它保存到您尝试更改属性的任何内容的 __dict__)。您必须修改 A.cv 才能修改所有内容,并且在 a(1|2|3) 中所做的任何更改都会覆盖所做的更改。

(Python 尝试使用a(1|2|3).__dict__,然后回退到A.__dict__。)

【讨论】:

    【解决方案2】:

    最后一个例子解释了 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)
    

    【讨论】:

      【解决方案3】:

      如果您不打算通过实例使用类属性,则可以有效地使用它们。例如,我喜欢在类属性中管理同一类的一组对象。如果你听说过 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)
      

      您是否看到使用类方法和属性可以如此轻松地管理对象?

      【讨论】:

        猜你喜欢
        • 2018-02-05
        • 1970-01-01
        • 2014-08-20
        • 2021-09-20
        • 1970-01-01
        • 1970-01-01
        • 2021-12-02
        • 1970-01-01
        相关资源
        最近更新 更多