1

我通常在 Python 中声明要在实例中使用的类变量的方式如下:

class MyClass(object):
  def __init__(self):
    self.a_member = 0

my_object = MyClass()
my_object.a_member    # evaluates to 0

但以下也有效。这是不好的做法吗?如果是这样,为什么?

class MyClass(object):
  a_member = 0

my_object = MyClass()
my_object.a_member    # also evaluates to 0

第二种方法在 Zope 各处都在使用,但我在其他任何地方都没有见过。这是为什么?

编辑:作为对 sr2222 答案的回应。我知道两者本质上是不同的。但是,如果该类仅用于实例化对象,则两者的工作方式相同。那么使用类变量作为实例变量是不是很糟糕?感觉会是这样,但我无法解释为什么。

4

4 回答 4

7

问题是这是本身的属性还是特定对象的属性。如果整个类的事物都具有某个属性(可能有一些小例外),那么一定要为该类分配一个属性。如果某些奇怪的对象或子类在此属性上有所不同,则可以根据需要对其进行覆盖。此外,这比为每个对象分配一个基本恒定的属性更节省内存;只有类__dict__具有该属性的单个条目,并且__dict__每个对象的 可能保持为空(至少对于该特定属性)。

简而言之,您的两个示例都是非常惯用的代码,但它们在机器级别和人类语义级别上的含义有些不同。


让我解释一下:

>>> class MyClass(object):
...       a_member = 'a'
...
>>> o = MyClass()
>>> p = MyClass()
>>> o.a_member
'a'
>>> p.a_member
'a'
>>> o.a_member = 'b'
>>> p.a_member
'a'

在第二行,您正在设置“类属性”。这实际上是名为“MyClass”的对象的属性。它存储为MyClass.__dict__['a_member'] = 'a'. 在后面的几行中,您将object属性设置o.a_member为。这完全等同于o.__dict__['a_member'] = 'b'。可以看到,这与单独的字典无关p.__dict__。访问a_memberp 时,在对象字典中找不到它,并推迟到它的类字典:MyClass.a_member. 这就是为什么修改 的属性o不会影响 的属性p,因为它不会影响 的属性MyClass

于 2012-08-20T03:12:35.273 回答
4

第一个是实例属性,第二个是类属性。它们根本不一样。实例属性附加到类型的实际创建对象,而类变量附加到类(类型)本身。

>>> class A(object):
...     cls_attr = 'a'
...     def __init__(self, x):
...         self.ins_attr = x
...
>>> a1 = A(1)
>>> a2 = A(2)
>>> a1.cls_attr
'a'
>>> a2.cls_attr
'a'
>>> a1.ins_attr
1
>>> a2.ins_attr
2
>>> a1.__class__.cls_attr = 'b'
>>> a2.cls_attr
'b'
>>> a1.ins_attr = 3
>>> a2.ins_attr
2
于 2012-08-20T02:59:37.467 回答
0

I have my answer! I owe to @mjgpy3's reference in the comment to the original post. The difference comes if the value assigned to the class variable is MUTABLE! THEN, the two will be changed together. The members split when a new value replaces the old one

>>> class MyClass(object):
...   my_str = 'a'
...   my_list = []
... 
>>> a1, a2 = MyClass(), MyClass()
>>> a1.my_str # This is the CLASS variable.
'a'
>>> a2.my_str # This is the exact same class variable.
'a'
>>> a1.my_str = 'b' # This is a completely new instance variable. Strings are not mutable.
>>> a2.my_str # This is still the old, unchanged class variable.
'a'
>>> a1.my_list.append('w') # We're changing the mutable class variable, but not reassigning it.
>>> a2.my_list # This is the same old class variable, but with a new value.
['w']

Edit: this is pretty much what bukzor wrote. They get the best answer mark.

于 2012-08-20T03:28:37.533 回答
0

即使您从不修改对象的内容,两者也不能互换。我理解它的方式,访问类属性比访问实例属性稍慢,因为解释器本质上必须采取额外的步骤来查找类属性。

实例属性

“什么a.thing?”

类属性

“什么a.thing?哦,a没有实例属性thing,我去查一下它的类……”

于 2012-08-20T03:15:23.330 回答