簡體   English   中英

舊式的班級作品,新式的班級破裂

[英]An old-style class works, new-style class broken

class A_old:
    def __getattr__(self, attr):
        print 'getattr', attr
        return super(A_old, self).__getattr__(attr)  # <-- note: don't do this!
    def __trunc__(self):
        return 3

class A_new(object):
    def __getattr__(self, attr):
        print 'getattr', attr
        return super(A_new, self).__getattr__(attr)
    def __trunc__(self):
        return 3

舊樣式的類有效,而新樣式的類無效。

>>> range(A_old())
getattr __int__
[0, 1, 2]
>>> range(A_new())
TypeError: range() integer end argument expected, got A_new.

為什么?


注意 :我使用的是2.7以上版本。 在Python 3中,這都不適用。在Python 3中, range被記錄為對__index__響應,而老式的類已經__index__了挪威藍。

老式類實現了另一種方法來測試是否可以轉換為數字,如果__int__不存在,則該方法支持使用__trunc__

range() (Python 2) 使用Py_TYPE(arg)->tp_as_number->nb_int()將值轉換為整數,該整數大致 (但不完全),類似於使用int() 因此,我們必須在這里查看新舊類的nb_int()插槽。

舊式類nb_int插槽實現instance_int() ,它使用hasattr() (或更確切地說是C等效項 )來測試__int__

if (PyObject_HasAttr((PyObject*)self, int_name))
    return generic_unary_op(self, int_name);

hasattr()吞下所有異常,包括舊類拋出的TypeError

>>> A_old().__int__
getattr __int__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in __getattr__
TypeError: super() argument 1 must be type, not classobj

因為hasattr()吞下了異常,所以hasattr()返回False

>>> hasattr(A_old(), '__int__')
getattr __int__
False

然后instance_int()的下一行使用__trunc__

truncated = _instance_trunc(self);

當您要求__trunc__時,新型類也永遠不會使用nb_int 他們想要__int__或破產。 那是因為它們直接支持插槽。 tp_as_number->nb_int() 直接調用__int__ __getattribute__ (如果有)(完全繞過__getattribute__ )。

請注意,當顯式使用int()進行轉換時,底層的C代碼將顯式查找__trunc__屬性(僅當沒有tp_as_number->nb_int()插槽可用時才使用該屬性),但是至少它不會使用hasattr() 這意味着在新樣式的類上使用int()仍然有效:

>>> int(A_new())
3

在Python 3中,所有__trunc__使用__trunc__將其視為適當的特殊方法

暫無
暫無

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

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