【问题标题】:Compound assignment to Python class and instance variables对 Python 类和实例变量的复合赋值
【发布时间】:2011-01-26 08:05:08
【问题描述】:

我一直在尝试理解 Python 对类和实例变量的处理。特别是,我发现this answer 很有帮助。基本上它说,如果您声明一个类变量,然后对[instance].property 进行赋值,您将完全分配给一个不同的变量——一个位于与类变量不同的命名空间中的变量。

然后我考虑了——如果我希望我的类的每个实例都有一个具有某些默认值(比如零)的成员,我应该这样做:

class Foo:
    num = 0

还是这样?

class Foo:
    def __init__(self):
        self.num = 0

根据我之前阅读的内容,我认为第二个示例将初始化“正确”变量(实例而不是类变量)。但是,我发现第一种方法也很有效:

class Foo:
    num = 0

bar = Foo()
bar.num += 1 # good, no error here, meaning that bar has an attribute 'num'
bar.num
>>> 1
Foo.num
>>> 0 # yet the class variable is not modified! so what 'num' did I add to just now?

那么.. 为什么这行得通?我没有得到什么? FWIW,我之前对 OOP 的理解来自 C++,所以类比解释(或指出它崩溃的地方)可能会有用。

【问题讨论】:

    标签: python class


    【解决方案1】:

    就我个人而言,我发现 Shalabh Chaturvedi 的 these documents 在这个主题方面非常有用且信息丰富。

    bar.num += 1bar.num = bar.num + 1 的简写。这是在右侧拾取类变量Foo.num,并将其分配给实例变量bar.num

    【讨论】:

    • 当我说类/实例variable我真正的意思是attribute
    【解决方案2】:

    在下面的代码中,num 是一个类成员。

    class Foo:
        num = 0
    

    C++ 等价物类似于

    struct Foo {
      static int num;
    };
    
    int Foo::num = 1;
    

    class Foo:
        def __init__(self):
            self.num = 0
    

    self.num 是实例成员(selfFoo 的实例)。

    在 C++ 中,类似于

    struct Foo {
      int num;
    };
    

    我相信 Python 允许您拥有一个类成员和一个实例成员共享相同的名称(C++ 不允许)。所以当你做bar = Foo()时,barFoo的一个实例,所以bar.num += 1,你增加实例成员。

    【讨论】:

    • 您的 C++ 等效项似乎将 num 的值增加了 1! :)
    【解决方案3】:
    bar.num += 1
    

    在“bar”实例上创建一个新的实例变量“num”,因为它还不存在(然后将此值加 1)

    一个例子:

    class Foo:
      def __init__(self):
        self.num= 1
    
    bar = Foo()
    print bar.num
    

    这会打印 1

    print bar.foo
    

    这会按预期给出错误:Traceback(最近一次调用最后一次): 文件“”,第 1 行,在 AttributeError: Foo 实例没有属性 'foo'

    bar.foo = 5
    print bar.foo
    

    现在打印 5

    所以在您的示例中会发生什么: bar.num 被解析为 Foo.num 因为只有一个类属性。然后 foo.num 实际上是创建的,因为您为其分配了一个值。

    【讨论】:

    • uh...bar.foo += 1 也给出了一个错误,因此很明显 Python 不会在包含 '+=' 的表达式中遇到它们时创建实例变量。我的问题是,创建类变量如何导致相应实例变量的初始化?
    • @int3。这与如何解析属性有关。访问属性时,首先搜索对象(在这种情况下为num),然后在这种情况下搜索对象的类型(Foo),然后是对象类型的基类及其基类。如果没有找到匹配的属性 AttributeError 则引发。
    • 确实如此,然后创建实例变量,因为我们为其赋值。我将更新示例
    【解决方案4】:

    搜索这个问题,这个 StackOverflow 问题和 Guido van Rossum (1, 2) 的两张(相当陈旧但有效的)幻灯片都出现了。 Guido 的幻灯片指出,这种行为与 Python 访问属性的搜索顺序有关(在上面的示例中是 num)。认为将两者放在一起会很好:)

    【讨论】:

      【解决方案5】:

      我认为您刚刚在 Python 中发现了一个错误。 bar.num += 1 应该是一个错误,而是在对象 bar 中创建一个属性 num,与 Foo.num 不同。

      这是一种非常奇怪的行为。

      【讨论】:

      • @Morgaelyn:不,这不是错误,这是预期的行为。阅读 python 类docs.python.org/tutorial/classes.html
      • 正如 MattH 所说,这是有意并记录在案的行为。所以,不,这不是错误。
      • 现在我明白了。 b.num += 1 与 b.num = b.num + 1 相同。第一个 b.num 引用对象属性并被创建。第二个 b.num 实际上是指 Foo.num 并且是 0。现在这是有道理的。我仍然认为这是语言的设计错误。很容易假设 b.num += 1 中的 b.num 总是引用同一个变量,但实际上它同时引用了两个不相关的变量。
      • 不是设计错误。这是一个实现细节。您通常不会使用实例属性隐藏类属性。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-31
      • 1970-01-01
      • 2015-05-23
      • 1970-01-01
      • 2016-01-24
      • 2013-09-13
      • 1970-01-01
      相关资源
      最近更新 更多