![](/img/trans.png)
[英]Is it safe to make an old-style class into a new-style class using Multiple Inheritance?
[英]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.