处理Python中的类变量的方式对我没有任何意义。看来类变量的范围取决于它的类型!原始类型被视为实例变量,复杂类型被视为类变量:
>>> class A(object):
... my_class_primitive = True
... my_class_object = ['foo']
...
>>> a = A()
>>> a.my_class_primitive, a.my_class_object
(True, ['foo'])
>>> b = A()
>>> b.my_class_primitive, b.my_class_object
(True, ['foo'])
>>> a.my_class_object.append('bar')
>>> b.my_class_primitive, b.my_class_object
(True, ['foo', 'bar'])
>>> a.my_class_primitive = False
>>> b.my_class_primitive, b.my_class_object
(True, ['foo', 'bar'])
>>> a.my_class_primitive, a.my_class_object
(False, ['foo', 'bar'])
有人可以解释以下内容:
答案 0 :(得分:1)
该功能作为Python类定义的缓存形式存在。
类定义中定义的属性本身被视为类的static
属性。例如它们不应该被修改。
我确信假设您遵循修改静态属性的最佳实践做出了决定;)
答案 1 :(得分:1)
你应该做的正确比较是:
>>> class A(object):
my_class_primitive = True
my_class_object = ['foo']
>>> a = A()
>>> b = A()
>>> a.my_class_primitive = False
>>> a.my_class_object = ['bar']
>>> b.my_class_primitive, b.my_class_object
(True, ['foo'])
这不是你所想的。 “奇怪”行为是由可变性的概念引起的。也就是说,当您将不同的对象分配给my_class_object
时,您正在更改分配给my_class_primitive
属性的对象。在第一种情况下,它适用于所有实例(因为这是一个对象),而在第二种情况下,它适用于单个实例(您正在更改其属性)。
在这种困惑中,你并不孤单。要查看可变性如何影响代码的另一个示例,您可以查看:"Least Astonishment" and the Mutable Default Argument
答案 2 :(得分:1)
>>> class A(object):
... my_class_primitive = True
... my_class_object = ['foo']
类属性存储在A.__dict__
:
>>> A.__dict__
>>> <dictproxy {'__dict__': <attribute '__dict__' of 'A' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'A' objects>,
'my_class_object': ['foo'],
'my_class_primitive': True}>
>>> a = A()
>>> a.my_class_primitive, a.my_class_object
(True, ['foo'])
>>> b = A()
>>> b.my_class_primitive, b.my_class_object
(True, ['foo'])
声明a.my_class_object.append('bar')
变异列表A.my_class_object
。它检索现有列表a.my_class_object
,然后调用其append
方法:
>>> a.my_class_object.append('bar')
所以b.my_class_object
也会受到影响:
>>> b.my_class_primitive, b.my_class_object
(True, ['foo', 'bar'])
作业a.my_class_primitive = False
向a
添加新的实例属性。它将键值对放在a.__dict__
:
>>> a.my_class_primitive = False
>>> a.__dict__
>>> {'my_class_primitive': False}
所以b
不受影响:
>>> b.my_class_primitive, b.my_class_object
(True, ['foo', 'bar'])
Python's attribute lookup rules导致a.my_class_primitive
返回a.__dict__
中找到的密钥的值,而不是A.__dict__
中找到的密钥的值:
>>> a.my_class_primitive, a.my_class_object
(False, ['foo', 'bar'])
答案 3 :(得分:1)
这不是原始的和复杂的。当您追加到a.my_class_object
时,您将修改现有对象。分配给变量时,不要对其进行修改。如果您像对待布尔值那样对待它们,则列表存在相同的“问题”:
>>> class Foo(object):
... x = []
...
>>> i1 = Foo()
>>> i2 = Foo()
>>>
>>> i1.x = 5
>>>
>>> print(i2.x)
[]
当获取 i1.x
时,Python会查看i1.__dict__
的属性。如果它在那里找不到它,它会查看该对象的每个父类的__dict__
,直到它(或抛出AttributeError
)。返回的对象根本不必是i1
的属性。
当您将分配给i1.x
时,您专门指定给i1.x
。
要修改类属性,请参阅类,而不是实例:
>>> class Foo(object):
... x = 2
...
>>> i1 = Foo()
>>>
>>> Foo.x = 5
>>>
>>> print(i1.x)
5
答案 4 :(得分:1)
原语和复杂类型之间的区别不在于,分配和修改变量之间存在差异。
如果使用instance.name = value
,即使已存在同名的类属性,也始终会分配新的实例变量。
例如:
>>> class A(object):
... my_class_primitive = True
... my_class_object = ['foo']
...
>>> a = A()
>>> a.my_class_primitive = False
>>> a.__class__.my_class_primitive
True
>>> a.__dict__
{'my_class_primitive': False}
此处与附加到列表的行为的不同之处在于,当您对类实例执行属性查找时,它将首先查找实例变量,如果没有具有该名称的实例变量,它将查找类属性。例如:
>>> a.my_class_object is a.__class__.my_class_object
True
因此,当您执行a.my_class_object.append(x)
时,您正在修改任何实例可以访问的class属性。如果您要执行a.my_class_object = x
,那么它不会以任何方式修改类属性,它只会创建一个新的实例变量。