type(obj) 和 obj.__class__ 的区别

85 投票
6 回答
14434 浏览
提问于 2025-04-15 12:34

在 Python 中,type(obj)obj.__class__ 这两个东西有什么区别呢?有没有可能出现 type(obj) is not obj.__class__ 的情况呢?

我想写一个函数,可以对传入的对象进行通用处理,并且希望用一个默认值 1,类型和另一个参数相同。下面的变体 #1 或 #2,哪个能正确工作呢?

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

6 个回答

35

老式类真是个麻烦,唉:

>>> class old: pass
... 
>>> x=old()
>>> type(x)
<type 'instance'>
>>> x.__class__
<class __main__.old at 0x6a150>
>>> 

在Python 3中就没这个问题了,因为现在所有的类都是新式类;-)。

在Python 2中,只有当一个类继承自另一个新式类(包括object和一些内置类型,比如dictlistset等)时,它才算是新式类,或者如果它设置了__metaclass__type,也算是新式类。

80

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

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()

输出:

<class '__main__.ClassB'>
<class '__main__.ClassA'>
ClassB

原因是 ClassB 覆盖了 __class__ 描述符,但对象内部的类型字段并没有改变。type(instance) 是直接从那个类型字段读取的,所以它返回的是正确的值。而 instance.__class__ 则是指向新的描述符,这个描述符替代了 Python 提供的原始描述符,它是读取内部类型字段的。与其读取那个内部类型字段,它返回的是一个硬编码的值。

21

type(obj)type.__class__ 在 Python 2 的旧式类中表现得不一样:

>>> 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
>>> type(ci)
<type 'instance'>
>>> ci.__class__
<class __main__.c at 0x7f437cee8600>

这个问题在 文档中有解释

对于旧式类来说,x.__class__ 会给出 x 的类,但 type(x) 总是返回 <type 'instance'>。这反映了一个事实:所有旧式实例,不管它们属于哪个类,都是用一个叫做 instance 的内置类型来实现的。

撰写回答