类型(obj)和 obj.__ class__ 之间的区别

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
12822 次浏览

老式的课程是问题所在,唉:

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

在 Python3中没有问题,因为现在所有的类都是新样式的; ——)。

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

type(obj)type.__class__在旧样式类中的行为不同:

>>> 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(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 提供的原始描述符,后者读取内部类型字段。它不读取内部类型字段,而是返回一个硬编码值。

顺便说一句,姜戈会这么做。

>>> from django.core.files.storage import default_storage
>>> type(default_storage)
django.core.files.storage.DefaultStorage
>>> default_storage.__class__
django.core.files.storage.FileSystemStorage

作为一个认知能力有限的人,为了完成工作只是想弄清楚发生了什么... 这很令人沮丧。

代理对象(使用弱引用)有一个有趣的边缘情况:

>>> import weakref
>>> class MyClass:
...     x = 42
...
>>> obj = MyClass()
>>> obj_proxy = weakref.proxy(obj)
>>> obj_proxy.x  # proxies attribute lookup to the referenced object
42
>>> type(obj_proxy)  # returns type of the proxy
weakproxy
>>> obj_proxy.__class__  # returns type of the referenced object
__main__.MyClass
>>> del obj  # breaks the proxy's weak reference
>>> type(obj_proxy)  # still works
weakproxy
>>> obj_proxy.__class__  # fails
ReferenceError: weakly-referenced object no longer exists