__ipow__ 当左侧对象 returns 未实现时引发 TypeError
__ipow__ raising TypeError when left-hand-side object returns NotImplemented
如果我有两个对象 A
和 B
,我可以 return NotImplemented
用于 A
的 __iadd__
方法并且有B
使用它的 __radd__
方法修改 A
。
>>> 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__
除外,其中引发了 TypeError
。
>>> 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()
requires numeric data 吗?最好的解决方法是什么?
Python 3.10+
此错误已在 Python 3.10
中修复
Due to a bug in the dispatching mechanism for **=
,
a class that defines __ipow__()
but returns NotImplemented
would fail to fall back to x.__pow__(y)
and y.__rpow__(x)
.
This bug is fixed in Python 3.10.
代码段现在按预期运行:
>>> a = A(2)
>>> b = B(2)
>>> a **= b
>>> a.val
4
这看起来像是一个错误,因为二进制和三元运算的代码之间存在不一致(**=
由于与 3 参数 pow
共享代码而由三元运算逻辑处理) .二进制就地操作通过 binary_iop1
,如果就地处理程序 returns 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 参数 pow
所需的代码差异,**=
无法通过该代码路径,因此它具有 its own ad-hoc handling:
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), "**=");
}
}
此临时处理提交到就地或非就地端,如果就地处理程序无法处理,则不会回退。
如果我有两个对象 A
和 B
,我可以 return NotImplemented
用于 A
的 __iadd__
方法并且有B
使用它的 __radd__
方法修改 A
。
>>> 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__
除外,其中引发了 TypeError
。
>>> 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()
requires numeric data 吗?最好的解决方法是什么?
Python 3.10+
此错误已在 Python 3.10
中修复Due to a bug in the dispatching mechanism for
**=
, a class that defines__ipow__()
but returns NotImplemented would fail to fall back tox.__pow__(y)
andy.__rpow__(x)
. This bug is fixed in Python 3.10.
代码段现在按预期运行:
>>> a = A(2)
>>> b = B(2)
>>> a **= b
>>> a.val
4
这看起来像是一个错误,因为二进制和三元运算的代码之间存在不一致(**=
由于与 3 参数 pow
共享代码而由三元运算逻辑处理) .二进制就地操作通过 binary_iop1
,如果就地处理程序 returns 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 参数 pow
所需的代码差异,**=
无法通过该代码路径,因此它具有 its own ad-hoc handling:
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), "**=");
}
}
此临时处理提交到就地或非就地端,如果就地处理程序无法处理,则不会回退。