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,其中range
被记录为响应__index__
而旧式类已经成为挪威蓝色的方式。 子>
答案 0 :(得分:0)
旧式类实现了一种不同的方法来测试是否可以转换为数字,如果__trunc__
不存在则支持使用__int__
。
range()
(Python 2),uses Py_TYPE(arg)->tp_as_number->nb_int()
将值转换为整数,大致,但不完全,就像使用int()
一样。因此,我们必须在这里查看旧式和新式类的nb_int()
插槽。
旧式类implement the nb_int
slot为instance_int()
,使用 hasattr()
(或更确切地说,C equivalent)来测试{{1} }}:
__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__
进行转换时,底层C代码将显式查找int()
属性(仅在没有__trunc__
插槽可用时使用它),但是至少它不会使用tp_as_number->nb_int()
。这意味着在新式课程中使用hasattr()
仍然有效:
int()
在Python 3中,>>> int(A_new())
3
的所有使用都将其视为正确的special method。