首页 > 解决方案 > __ipow__ 当左侧对象返回 NotImplemented 时引发 TypeError

问题描述

如果我有两个对象AB,我可以返回NotImplementedforA__iadd__方法并使用它的方法进行B修改。A__radd__

>>> class A():
...     def __init__(self, val):
...         self.val = val
...     def __iadd__(self, other):
...         return NotImplemented
...     def __ipow__(self, other):
...         return NotImplemented 
... 
>>> class B():
...     def __init__(self, val):
...         self.val = val
...     def __radd__(self, other):
...         return A(other.val + self.val)
...     def __rpow__(self, other):
...         return A(other.val ** self.val) 
... 
>>> a = A(2)
>>> b = B(2)
>>> a += b
>>> a.val
4

这似乎适用于所有就地运算符,除了__ipow__aTypeError被提出的地方。

>>> a **= b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for ** or pow(): 'A' and 'B'

为什么这里的行为不同?这是因为pow()需要数字数据而失败吗?最好的解决方法是什么?

标签: python-3.xoperatorsin-place

解决方案


Python 3.10+

此错误已在Python 3.10中修复

由于 的调度机制中的错误**=,定义__ipow__()但返回 NotImplemented 的类将无法回退到x.__pow__(y)and y.__rpow__(x)。此错误已在 Python 3.10 中修复。

该片段现在按预期工作:

>>> a = A(2)
>>> b = B(2)
>>> a **= b
>>> a.val
4

由于二进制和三元运算的代码之间的不一致,这看起来像是一个错误(**=由于与 3-argument 共享代码而由三元运算逻辑处理pow)。二进制就地操作通过binary_iop1,如果就地处理程序返回,则有代码回退到非就地例程NotImplemented

static PyObject *
binary_iop1(PyObject *v, PyObject *w, const int iop_slot, const int op_slot)
{
    PyNumberMethods *mv = v->ob_type->tp_as_number;
    if (mv != NULL) {
        binaryfunc slot = NB_BINOP(mv, iop_slot);
        if (slot) {
            PyObject *x = (slot)(v, w);
            if (x != Py_NotImplemented) {
                return x;
            }
            Py_DECREF(x);
        }
    }
    return binary_op1(v, w, op_slot);
}

但由于 3-argument 所需的代码差异pow**=无法通过该代码路径,因此它有自己的临时处理

PyObject *
PyNumber_InPlacePower(PyObject *v, PyObject *w, PyObject *z)
{
    if (v->ob_type->tp_as_number &&
        v->ob_type->tp_as_number->nb_inplace_power != NULL) {
        return ternary_op(v, w, z, NB_SLOT(nb_inplace_power), "**=");
    }
    else {
        return ternary_op(v, w, z, NB_SLOT(nb_power), "**=");
    }
}

这种临时处理提交到就地或非就地方面,如果就地处理程序无法处理它,则没有回退。


推荐阅读