关于python:type(obj) 和 obj.__class__的区别

 2019-04-22 

Difference between type(obj) and obj.__class__

type(obj)obj.__class__有什么区别?有没有可能出现type(obj) is not obj.__class__

我想编写一个在所提供对象上一般工作的函数,使用与另一个参数相同类型的默认值1。下面的哪一个变化,1或2,将做正确的事情?

1
2
3
4
def f(a, b=None):
  if b is None:
    b = type(a)(1) # #1
    b = a.__class__(1) # #2

这是一个古老的问题,但似乎没有一个答案提到这一点。在一般情况下,一个新类型的类可能对type(instance)instance.__class__有不同的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ClassA(object):
    def display(self):
        print("ClassA")

class ClassB(object):
    __class__ = ClassA

    def display(self):
        print("ClassB")

instance = ClassB()

print(type(instance))
print(instance.__class__)
instance.display()

输出:

1
2
3
<class '__main__.ClassB'>
<class '__main__.ClassA'>
ClassB

原因是ClassB正在重写__class__描述符,但是对象中的内部类型字段没有更改。type(instance)直接从该类型字段中读取,因此返回正确的值,而instance.__class__是指替换python提供的原始描述符的新描述符,后者读取内部类型字段。它不是读取内部类型字段,而是返回一个硬编码值。


旧风格的课程是问题所在,叹息:

1
2
3
4
5
6
7
8
>>> class old: pass
...
>>> x=old()
>>> type(x)
<type 'instance'>
>>> x.__class__
<class __main__.old at 0x6a150>
>>>

在python 3中没有问题,因为所有类现在都是新样式;-)。

在python 2中,类只有从另一个新的样式类(包括object和各种内置类型(如dictlistset…)继承,或者隐式或显式地将__metaclass__设置为type时,才是新样式。


对于旧式类,type(obj)type.__class__的行为不同:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> class a(object):
...     pass
...
>>> class b(a):
...     pass
...
>>> class c:
...     pass
...
>>> ai=a()
>>> bi=b()
>>> ci=c()
>>> type(ai) is ai.__class__
True
>>> type(bi) is bi.__class__
True
>>> type(ci) is ci.__class__
False