簡體   English   中英

type(instance) 何時與 instance.__class__ 不同?

[英]When is type(instance) different from instance.__class__?

Python 具有內置函數type

type對象

使用一個參數,返回object的類型。 返回值是一個類型對象,通常與object.__class__返回的object.__class__相同。

Python 還具有特殊屬性__class__

實例。 __class__

類實例所屬的類。

我曾經相信他們都指的是同一個對象。 然而,在方法abc.ABCMeta.__instancecheck__有一個檢查它們是否相同:

    def __instancecheck__(cls, instance):
        """Override for isinstance(instance, cls)."""
        # Inline the cache checking
        subclass = instance.__class__
        # […]
        subtype = type(instance)
        if subtype is subclass:
        # […]

type(instance)何時與instance.__class__不同?

type(instance)instance.__class__可能不同,即使是新式 classes ,正如 Guido van Rossum 在PEP 3119 中提到的:

此外, isinstance(x, B)等效於issubclass(x.__class__, B) or issubclass(type(x), B) (可能type(x)x.__class__不是同一個對象,例如當x是代理對象時。)

type(instance)

object實例的真實類存儲在實例上的__class__槽中(即在實例布局中的固定偏移量處)。 它只能通過數據描述符vars(object)['__class__'] (其方法__get__允許屬性檢索,其方法__set__允許屬性賦值,其方法__delete__禁止屬性刪除),或等效地通過內置函數訪問type (其單參數形式允許屬性檢索):

>>> class A: pass
... 
>>> a = A()
>>> vars(object)['__class__'].__get__(a)
<class '__main__.A'>
>>> type(a)
<class '__main__.A'>
>>> class B: pass
... 
>>> vars(object)['__class__'].__set__(a, B)
>>> vars(object)['__class__'].__get__(a)
<class '__main__.B'>
>>> type(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

instance.__class__

因此,如果一個實例不會覆蓋數據描述符vars(object)['__class__']具有類屬性), instance.__class__訪問真正類的instance (通過數據描述符):

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

但是如果一個實例覆蓋了數據描述符vars(object)['__class__'] class vars(object)['__class__'] (帶有一個類屬性), instance.__class__可能無法訪問instance的真實類(所以instance作為另一個類的instance出現):

>>> class A: __class__ = int
... 
>>> a = A()
>>> a.__class__
<class 'int'>
>>> type(a)
<class '__main__.A'>
>>> vars(a).update({'__class__': str})
>>> a.__class__ # the class attribute is not a data descriptor so is overridden
<class 'str'>
>>> type(a)
<class '__main__.A'>

這是舊樣式對象的情況(從無繼承)。 此類對象沒有__class__屬性。 我認為他們這樣做是為了防止錯誤。 Python 2.7 示例:

class A:
    pass

class B(object):
    pass

a = A()
b = B()

print(dir(a)) # ['__doc__', '__module__']
print(dir(b)) # ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

print(b.__class__) # <class '__main__.B'>
print(type(b))     # <class '__main__.B'>

#####################
# The intersting part
print(a.__class__) # __main__.A
print(type(a))     # <type 'instance'>


print(B.__class__) # <type 'type'>
print(type(B))     # <type 'type'>

print(type(A))      # <type 'classobj'>
#print(A.__class__) # AttributeError: class A has no attribute '__class__'

請參閱此了解更多信息:

注意:來自 cpython 的給定行上次在 2008 年( commit )發生了更改,因此這似乎真的是兼容性問題,或者他們只是忘記了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM