【问题标题】:What in the world is the attribute "__class__" in pythonpython中的属性“__class__”到底是什么?
【发布时间】:2011-11-09 05:30:58
【问题描述】:

我对 python 中的__class__ 有疑问。

文档说__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__这个属性到底是什么?

【问题讨论】:

  • +1 冗长的问题。
  • 另外,is 并没有像您认为的那样做,尤其是对于小整数。

标签: python class


【解决方案1】:

“根据上面的代码,我认为 NewCounter1.count 可能等于 NewCounter1._class_.count”

问题是,在你的问题中,这句话的那一刻,在唯一的说明之后:

NewCounter1 = counter()
NewCounter2 = counter()
NewCounter2.__class__.count = 3

已创建 NewCounter1NewCounter2
并修改了类属性 counter.count,
NewCounter1.countNewCounter2.count 对象都不存在,则 "equals" 没有实际意义。

.

查看 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属性实例 !

这怎么可能?

这是因为创建实例时没有分配给 _init_
内的“计数”标识符 -> NewCounter1中没有真正创建任何属性作为字段,也就是说没有创建INSTANCE属性。

后果就是当指令
print 'NewCounter1.count ==',NewCounter1.count
求值时,解释器在 NewCounter1 的命名空间中没有找到实例属性,然后去实例的类在该类的命名空间中搜索键“count”;在那里,它找到“count”作为 CLASS 属性的键,并且可以将对象 counter.count 的 VALUE 作为 VALUE 来显示以响应指令。

一个类实例有一个命名空间实现为字典,它是 搜索属性引用的第一个位置。当一个 在那里找不到属性,并且实例的类有 按该名称的属性,搜索继续类 属性。 http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy

所以,NewCounter1.count equals NewCounter1.__class__.count 这里的意思是 NewCounter1.count 的 VALUE,即使这个并不真正存在,也是类属性 NewCounter1 的 VALUE。 .count。这里的“is”是英语动词,不是语言中测试两个对象身份的特征is,它的意思是'被认为有'

NewCounter2.__class__.count = 3 执行时,只影响类属性counter.countNewCounter1NewCounter2 的命名空间保持为空,并遵循相同的机制去类中查找 counter.count 的值。

.

最后,当NewCounter2.count = 5被执行时,这一次实例属性count被创建为NewCounter2对象中的一个字段,并且'count'出现在NewCounter2 的命名空间。
它不会覆盖任何内容,因为实例的 __dict__
之前没有任何内容 没有其他更改会影响 NewCounter1counter.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 # &lt;&lt;=====
观察结果的变化

.

总之,重点不在于__class__,而在于搜索属性的机制,而这种机制在被忽略时会产生误导。

【讨论】:

  • 非常感谢您的耐心和热情。我不知道该说什么,但谢谢你。我真的知道这个机制。你说的很详细。再次感谢!
  • 谢谢。我喜欢详细检查事物,我喜欢写一些东西,试图以尽可能少的歧义和模糊的措辞产生清晰的文本。第一个提高了一个人的理解力,第二个允许测试这种理解是否扎实。祝你玩得愉快。
  • @eyquem 非常感谢。这个答案的信息非常丰富,我必须至少再读一次才能真正得到它。我希望所有答案都是如此高质量。
  • @eyquem 如果你不介意给小费,你会怎么处理这种情况?:首先,问一个问题。接下来,我开始写一个非常详细的答案,比如this。接下来,当我这样做时,其他人快速回答并且提问者接受了他们的回答。最后,我一无所有:(。有什么方法可以解决这种情况?既然你有接受的答案并且很详细,也许你有解决方案?我知道这是随机的,但提前谢谢。
  • @eyquem,你为什么在 display() 函数中计算 'nn' ?
【解决方案2】:

这一行:

NewCounter2.__class__.count = 3

更改counter 的静态count,但在这里:

NewCounter2.count = 5

NewCounter2 现在有自己的count 属性,可以隐藏静态count
所以这条线对NewCounter1没有影响。
这也是NewCounter2.__class__.count != NewCounter2.count的原因。

【讨论】:

  • 混淆似乎出现了,因为虽然你可以getcounter 实例上的属性counter.count,但你不能设置属性counter.count 直接在counter 的实例上。
【解决方案3】:

重新绑定(即分配)与类上的属性同名的对象上的属性会隐藏类上的属性。始终首先检查对象的属性,然后按 MRO 顺序检查类。

【讨论】:

    【解决方案4】:

    线

    NewCounter2.count = 5
    

    创建NewCounter2 的新实例级别属性。之后,您访问两个不同的属性(NewCounter2.count - 实例级别 attr 和 NewCounter2.__class__.count - 类级别 attr)会导致“奇怪”行为。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-09-10
      • 2020-07-17
      • 2012-04-18
      • 1970-01-01
      • 2012-06-23
      • 2016-08-15
      • 1970-01-01
      相关资源
      最近更新 更多