![](/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.