Python引用类和实例变量

时间:2016-03-12 00:34:14

标签: python python-2.7 variables reference namespaces

我已经阅读了许多关于Python中变量引用的问题,但是当我使用我不希望的代码并且它变得非常令人沮丧时,事情仍在发生。拿这段代码:

class MyClass(object) :

    class_var = "original message" # same for all objects

    def __init__(self, instance_var) :
        self.instance_var = instance_var #particular to the object

my_object = MyClass("my instance variable")
untouched_object = MyClass("another instance_var")

# Values of class_var and instance_var as expected

MyClass.class_var = "changed message"

# attribute class_var has changed for both objects

# This changes attribute class_var for my_object only
my_object.class_var = "I am my_object"

# THIS ONLY CHANGES ATTRIBUTE CLASS_VAR FOR UNTOUCHED_OBJECT ONLY!
MyClass.class_var = "Last message"

print MyClass.class_var #prints Last Message
print my_object.class_var #prints I am my_object ???
print untouched_object.class_var #prints Last Message

这里发生了什么?当我使用类变量创建某个类的对象时,似乎object.class_attribute最初包含指向Class.class_var的指针,我可以通过分配给Class.class_var来更改所有对象的属性。但是如果我明确地将object.class属性分配给其他某个值,则指针会消失,并且更改Class.class_var不再对该特定对象的更改产生影响,只会影响所有其他实例。这是什么意思?此外,为什么我们能够以这种方式访问​​和更改类属性,并且可能无法访问Python文档声称所有实例都知道的值?

2 个答案:

答案 0 :(得分:3)

当你这样做时

my_object.class_var = "I am my_object"

您创建了一个影响类属性my_object.class_var实例属性MyClass.class_var,因此对MyClass.class_var的后续分配似乎对{{my_object没有影响1}}。

根据其他语言的概念(例如引用和指针)尝试理解Python的数据模型可能会产生误导。尝试考虑名称,以及对象与名称的绑定。

因此,在您的示例中,MyClass.class_varmy_object.class_var最初都是绑定到class属性的名称,但是赋值将新的字符串对象绑定到名称my_object.class_var

我发现将(名称,对象)对视为字典中的(键,值)对会有所帮助。在某些情况下,这实际上是“幕后”发生的事情(例如,您自己创建的普通类),但在其他情况下(如Python的内置类型),由于效率原因,事情的实现方式略有不同。

您可能会发现这篇文章很有用:Facts and myths about Python names and values,由SO资深人士Ned Batchelder撰写。

答案 1 :(得分:2)

每个对象都有一个名为__dict__的变量,用于存储其成员变量的值,并按名称索引。每个类还有一个名为__dict__的变量,它存储由类名索引的类变量的值。当您使用a.v语法访问实例a的变量v时,Python首先查找a.__dict__['v']。如果没有这样的元素,它会在类字典中查找,即a.__class__.__dict__['v']。这就是类变量对该类的每个实例可见的方式。

但是当您分配给变量时,过程是不同的。类似a.v = 12的语句会导致Python执行a.__dict__['v'] = 12。它没有触及类字典。因此,正如您所期望的那样,该类的其他实例不受影响。现在,当您访问a.v时,您将获得值12,即您刚刚设置的特定于实例的值,而不是类变量的值。并且你不能再通过搞乱类变量来改变a.v,因为a现在有自己的名为'v'的变量。

这样做的结果是你应该主要使用类变量来设置“常量”,类似于你在C中使用枚举或#define的方式。这是一个你不打算改变的值。如果将类变量限制为该使用方式,则它们非常方便且易于理解。