一个旧式的作品,新式的类破碎

时间:2016-12-08 21:07:56

标签: python range python-2.x

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__而旧式类已经成为挪威蓝色的方式。

1 个答案:

答案 0 :(得分:0)

旧式类实现了一种不同的方法来测试是否可以转换为数字,如果__trunc__不存在则支持使用__int__

range()(Python 2),uses Py_TYPE(arg)->tp_as_number->nb_int()将值转换为整数,大致,但不完全,就像使用int()一样。因此,我们必须在这里查看旧式和新式类的nb_int()插槽。

旧式类implement the nb_int slotinstance_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