Python中的属性“__class__”究竟是什么?
我有一个关于 __class__
在 Python 中的问题。
文档上说 __class__
是一个类实例所属的类。于是我做了一系列实验:
class counter:
count = 0
def __init__(self):
self.__class__.count += 1
NewCounter1 = counter()
print NewCounter1.count #The result is 1
NewCounter2 = counter()
print NewCounter2.count #The result is 2
print NewCounter2.__class__.count is NewCounter2.count #result: True
一切都很顺利。
然后我输入了如下代码:
NewCounter2.__class__.count = 3
print NewCounter1.count #result:3
print NewCounter1.__class__.count #result:3
print NewCounter2.count #result:3
print NewCounter2.__class__.count #result:3
print NewCounter2.__class__.count is NewCounter2.count #result: True
从上面的代码来看,我以为 NewCounter1.count
可能等于 NewCounter1
,或者 __class__.count
,但是接下来的代码让我感到惊讶:
NewCounter2.count = 5
print NewCounter1.count #result:3
print NewCounter1.__class__.count #result:3
print NewCounter2.count #result:5
print NewCounter2.__class__.count #result:3
print NewCounter2.__class__.count is NewCounter2.count #result: False
为什么 NewCounter2.count
发生了变化,而 NewCounter2.__class__.count
仍然是 3?更有意思的是,当我改变了 NewCounter2.count
的值后,NewCounter2.__class__.count is NewCounter2.count
变成了 False
。这个 __class__
属性到底是什么呢?
4 个回答
如果你在一个对象上重新绑定(也就是给它赋值)一个和类里同名的属性,这样做会把类里的那个属性给遮住。也就是说,当你查找属性的时候,系统会先看看这个对象有没有这个属性,如果没有,再去查找它的类,按照类的继承顺序来找。
这一行:
NewCounter2.__class__.count = 3
改变了 counter
的静态 count
,但是在这里:
NewCounter2.count = 5
NewCounter2
现在有了自己的 count
属性,这个属性遮住了静态的 count
;
所以那一行对 NewCounter1
没有影响。
这也是为什么 NewCounter2.__class__.count != NewCounter2.count
的原因。
“从上面的代码来看,我觉得 NewCounter1.count 可能等于 NewCounter1._class_.count。”
问题在于,在你提问的这句话时,经过唯一的指令:
NewCounter1 = counter()
NewCounter2 = counter()
NewCounter2.__class__.count = 3
创建了 NewCounter1 和 NewCounter2
并且修改了类属性 counter.count,
此时并不存在 NewCounter1.count 或 NewCounter2.count,所以 “等于” 这个说法没有实际意义。
.
看看 NewCounter1 的创建过程,紧接着:
class counter:
count = 0
def __init__(self):
self.__class__.count += 1
print 'counter.count BEFORE ==',counter.count # The result is 0
NewCounter1 = counter()
print '\nNewCounter1.__dict__ ==',NewCounter1.__dict__ # The result is {}
print 'NewCounter1.count ==',NewCounter1.count # The result is 1
print 'counter.count AFTER ==',counter.count # The result is 1
NewCounter._dict_ 是实例 NewCounter1 的命名空间
print NewCounter1.count
打印的内容和 print counter.count
是一样的
但是,'count'(字符串 'count')并不在 NewCounter1 的命名空间中,也就是说在这个实例中没有 count 这个属性!
这怎么可能呢?
这是因为实例是在没有给 'count' 这个标识符赋值的情况下创建的
-> 这意味着在 NewCounter1 中并没有真正创建任何作为字段的属性,也就是说没有创建实例属性。
结果是,当执行指令
print 'NewCounter1.count ==',NewCounter1.count
时,解释器在 NewCounter1 的命名空间中找不到实例属性,然后就去实例的类中查找这个类的命名空间里的 'count';在那里它找到了 'count' 作为一个类属性的键,并可以获取对象 counter.count 的值来显示作为指令的响应。
一个类的实例有一个命名空间,这个命名空间实现为字典,是查找属性引用的第一个地方。当在这里找不到某个属性时,如果实例的类中有同名属性,查找就会继续进行到类属性。
http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy
所以,NewCounter1.count 等于 NewCounter1.__class__.count
在这里的意思是,虽然 NewCounter1.count 并不真正存在,但它的值是类属性 NewCounter1.class.count 的值。在这里,“是”是英语动词,而不是用来测试两个对象身份的语言特性 is,它的意思是“被认为是”。
当执行 NewCounter2.__class__.count = 3
时,只有类属性 counter.count 受到影响。NewCounter1 和 NewCounter2 的命名空间保持为空,查找 counter.count 的值的机制依然相同。
.
最后,当执行 NewCounter2.count = 5
时,这次在 NewCounter2 对象中创建了一个实例属性 count,并且 'count' 出现在 NewCounter2 的命名空间中。
这并不会覆盖任何东西,因为在实例的 __dict__
中之前没有任何内容。
没有其他的变化会影响 NewCounter1 和 counter.count。
以下代码更清楚地展示了执行过程中的事件:
from itertools import islice
class counter:
count = 0
def __init__(self):
print (' | counter.count first == %d at %d\n'
' | self.count first == %d at %d')\
% (counter.count,id(counter.count),
self.count,id(self.count))
self.__class__.count += 1 # <<=====
print (' | counter.count second == %d at %d\n'
' | self.count second == %d at %d\n'
' | id(counter) == %d id(self) == %d')\
% (counter.count,id(counter.count),
self.count,id(self.count),
id(counter),id(self))
def display(*li):
it = iter(li)
for ch in it:
nn = (len(ch)-len(ch.lstrip('\n')))*'\n'
x = it.next()
print '%s == %s %s' % (ch,x,'' if '__dict__' in ch else 'at '+str(id(x)))
display('counter.count AT START',counter.count)
print ('\n\n----- C1 = counter() ------------------------')
C1 = counter()
display('C1.__dict__',C1.__dict__,
'C1.count ',C1.count,
'\ncounter.count ',counter.count)
print ('\n\n----- C2 = counter() ------------------------')
C2 = counter()
print (' -------------------------------------------')
display('C1.__dict__',C1.__dict__,
'C2.__dict__',C2.__dict__,
'C1.count ',C1.count,
'C2.count ',C2.count,
'C1.__class__.count',C1.__class__.count,
'C2.__class__.count',C2.__class__.count,
'\ncounter.count ',counter.count)
print '\n\n------- C2.__class__.count = 3 ------------------------\n'
C2.__class__.count = 3
display('C1.__dict__',C1.__dict__,
'C2.__dict__',C2.__dict__,
'C1.count ',C1.count,
'C2.count ',C2.count,
'C1.__class__.count',C1.__class__.count,
'C2.__class__.count',C2.__class__.count,
'\ncounter.count ',counter.count)
print '\n\n------- C2.count = 5 ------------------------\n'
C2.count = 5
display('C1.__dict__',C1.__dict__,
'C2.__dict__',C2.__dict__,
'C1.count ',C1.count,
'C2.count ',C2.count,
'C1.__class__.count',C1.__class__.count,
'C2.__class__.count',C2.__class__.count,
'\ncounter.count ',counter.count)
结果
counter.count AT START == 0 at 10021628
----- C1 = counter() ------------------------
| counter.count first == 0 at 10021628
| self.count first == 0 at 10021628
| counter.count second == 1 at 10021616
| self.count second == 1 at 10021616
| id(counter) == 11211248 id(self) == 18735712
C1.__dict__ == {}
C1.count == 1 at 10021616
counter.count == 1 at 10021616
----- C2 = counter() ------------------------
| counter.count first == 1 at 10021616
| self.count first == 1 at 10021616
| counter.count second == 2 at 10021604
| self.count second == 2 at 10021604
| id(counter) == 11211248 id(self) == 18736032
-------------------------------------------
C1.__dict__ == {}
C2.__dict__ == {}
C1.count == 2 at 10021604
C2.count == 2 at 10021604
C1.__class__.count == 2 at 10021604
C2.__class__.count == 2 at 10021604
counter.count == 2 at 10021604
------- C2.__class__.count = 3 ------------------------
C1.__dict__ == {}
C2.__dict__ == {}
C1.count == 3 at 10021592
C2.count == 3 at 10021592
C1.__class__.count == 3 at 10021592
C2.__class__.count == 3 at 10021592
counter.count == 3 at 10021592
------- C2.count = 5 ------------------------
C1.__dict__ == {}
C2.__dict__ == {'count': 5}
C1.count == 3 at 10021592
C2.count == 5 at 10021568
C1.__class__.count == 3 at 10021592
C2.__class__.count == 3 at 10021592
counter.count == 3 at 10021592
.
一个有趣的事情是,在
self.count = counter.count
这一行之前添加一条指令
self.__class__.count += 1 # <<=====
来观察结果的变化。
.
总之,问题并不在于 __class__
,而在于查找属性的机制,当忽视这个机制时会造成误解。