实现数字方法总是返回NotImplemented



我正在编写一个新的扩展类型,但在设置数字运算(如加法/减法/乘法)时遇到了问题。我已经设法设置了一些就地操作,而不调用正常操作。

例如,我有一个功能:

static PyObject *
MyType_Mul(PyObject *v, PyObject *w)
{
PyErr_SetString(PyExc_ValueError, "testing");
return NULL;
}

我把它设置在这样的数字方法中:

static PyNumberMethods my_type_as_number = {
0,  /* nb_add */
0,  /* nb_sub */
(binaryfunc)MyType_Mul,  /* nb_mul */
...
0,  /* nb_in_place_add */
0,  /* nb_in_place_sub */
(binaryfunc)MyType_Mul,  /* nb_in_place_mul */
...
};

现在,当我尝试使用我的类型时,我会得到这种行为:

>>> from mytype import MyType
>>> a = MyType()
>>> a * 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for *: 'mytype.MyType' and 'int'
>>> 2 * a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for *: 'int' and 'mytype.MyType'

但是,如果我使用就地操作员:

>>> a *= 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: testing

如果我在对象上使用dir(),我可以看到__mul____rmul__方法(这意味着python可以看到它们),但似乎根本没有调用它们。使用a.__mul__(2)返回NotImplemented

还有:

>>> a.__mul__
<method-wrapper '__mul__' of mytype.MyType object at 0x7fc2ecc50468>
>>> a.__imul__
<method-wrapper '__imul__' of mytype.MyType object at 0x7fc2ecc50468>

所以,正如你所看到的,它们是完全相同的东西。

怎么回事?为什么相同的函数适用于在位运算符,而不适用于"正常"运算符?我也认为我可能使用了错误的插槽,但我仔细检查了一下,它是正确的,而且将其设置为nb_addnb_sub等也不起作用。

多亏了nneonneo的评论,我明白了问题所在。基本上我忘了设置Py_TPFLAGS_CHECKTYPES标志。

在我给出的描述中,有一些关于这种缺失的线索:

  1. 方法是同一个对象,但它对就地操作的作用不同
  2. 在评论中,我还说,例如,执行a*a会产生正确的结果,而执行a*different-type则不会

这显然意味着解释器在执行非原位操作时,会检查参数的类型,如果类型为MyType,则调用我的函数,否则返回NotImplemented

在文档中搜索一下,很容易看出这是数值方法的默认行为。

如果参数类型不属于同一类,则假定未实现该操作。

要允许不同类型一起"操作",必须在MyType:中设置Py_TPFLAGS_CHECKTYPES标志

static PyTypeObject MyType = {
PyObject_HEAD_INIT(&PyType_Type)
0,                         /*ob_size*/
"mytype.MyType",           /*tp_name*/
sizeof(MyTypeObject),      /*tp_basicsize*/
0,                         /*tp_itemsize*/
...
0,                         /*tp_repr*/
&mytype_as_number,         /*tp_as_number*/
0,                         /*tp_as_sequence*/
...
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,/*tp_flags*/
...
};

设置此标志后,解释器将不会检查类型,因此您必须手动处理它们。

相反,在位运算符总是允许不同的类型。我不知道为什么。

相关内容

  • 没有找到相关文章