什么时候类型(实例)不是实例.__ class__?

MSe*_*ert 9 python types python-3.x

Python具有内置函数type,每个实例也有一个__class__属性.我一般认为他们回归了同样的事情.甚至两个声音的文档都相似:

instance.__class__

类实例所属的类.

type(object)

使用一个参数,返回对象的类型.

但是,abc.ABCMeta.__instancecheck__检查它们是否相同(略微缩短):

subclass = instance.__class__
subtype = type(instance)
if subtype is subclass:
Run Code Online (Sandbox Code Playgroud)

什么时候不是这样的?什么时候type(instance)不一样instance.__class__

Mag*_*ero 4

type(instance)即使是新式的类,也instance.__class__可能会有所不同,正如 Guido van Rossum 在PEP 3119中提到的那样:

另外,isinstance(x, B)相当于issubclass(x.__class__, B) or issubclass(type(x), B). (有可能type(x)x.__class__不是同一个对象,例如何时x是代理对象。)

例如,weakref.proxy标准库的函数创建代理对象。

>>> import weakref
>>> class A: pass
... 
>>> a = A()
>>> type(weakref.proxy(a))
<class 'weakproxy'>
>>> weakref.proxy(a).__class__
<class '__main__.A'>
>>> repr(weakref.proxy(a))
'<weakproxy at 0x10065ab30 to A at 0x1006534c0>'
Run Code Online (Sandbox Code Playgroud)

__repr__请注意,代理对象的方法的实现使用type(instance),而不是instance.__class__,因为该方法的主要目的__repr__是在调试时提供足够的信息来重新创建对象。

type(instance)

实例的真实类object存储在实例上的槽中__class__ 即,在实例布局中的固定偏移处)。它只能通过数据描述符 vars(object)['__class__'](其方法__get__允许属性检索,其方法__set__允许属性赋值,其方法__delete__禁止属性删除)或等效地通过内置函数type(其单参数形式允许属性检索)来访问:

>>> class A: pass
... 
>>> a = A()
>>> type(a)
<class '__main__.A'>
>>> vars(object)['__class__'].__get__(a)
<class '__main__.A'>
>>> class B: pass
... 
>>> vars(object)['__class__'].__set__(a, B)
>>> type(a)
<class '__main__.B'>
>>> vars(object)['__class__'].__get__(a)
<class '__main__.B'>
>>> vars(object)['__class__'].__delete__(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't delete __class__ attribute
Run Code Online (Sandbox Code Playgroud)

instance.__class__

如果数据描述符vars(object)['__class__']没有在object子类中被重写,则通过数据描述instance.__class__符访问真实类:instance

>>> class A: pass
... 
>>> a = A()
>>> type(a)
<class '__main__.A'>
>>> a.__class__
<class '__main__.A'>
>>> class B: pass
... 
>>> a.__class__ = B
>>> type(a)
<class '__main__.B'>
>>> a.__class__
<class '__main__.B'>
>>> del a.__class__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't delete __class__ attribute
Run Code Online (Sandbox Code Playgroud)

但如果数据描述符在子类vars(object)['__class__']中被重写objectinstance.__class__则无法访问真正的类instance。此外,如果覆盖不是数据描述符,它本身可以在以下位置被覆盖instance

>>> class A: __class__ = int  # overrides vars(object)['__class__']
... 
>>> a = A()
>>> type(a)
<class '__main__.A'>
>>> a.__class__
<class 'int'>
>>> a.__class__ = str  # overrides vars(A)['__class__'] (not a data descriptor)
>>> type(a)
<class '__main__.A'>
>>> a.__class__
<class 'str'>
>>> del a.__class__
>>> type(a)
<class '__main__.A'>
>>> a.__class__
<class 'int'>
Run Code Online (Sandbox Code Playgroud)