4

新しい拡張タイプを書いていますが、数値演算 (加算/減算/乗算など) の設定に問題があります。通常の操作は呼び出されませんが、いくつかのインプレース操作を設定することができました。

たとえば、次の関数があります。

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_add機能nb_subしません。

4

1 に答える 1

3

nneonneo さんのコメントのおかげで、何が悪いのか理解できました。Py_TPFLAGS_CHECKTYPES基本的に、フラグを設定するのを忘れていました。

私が提供した説明には、この不在についていくつかの手がかりがあります。

  1. メソッドは同じオブジェクトですが、インプレース操作では異なる動作をします
  2. コメントの中で、たとえば、実行a*aすると正しい結果が得られるが、実行a*different-typeしないことも述べました。

これは、インタープリターが非インプレース操作を行うときに引数の型をチェックし、型が の場合は my 関数を呼び出しMyType、そうでない場合は を返すことを明確に意味しますNotImplemented

ドキュメントを少し検索すると、これが数値メソッドのデフォルトの動作であることが簡単にわかります。

引数の型が同じクラスでない場合、操作は実装されていないと見なされます。

異なるタイプを一緒に「操作」できるようにするには、次のPy_TPFLAGS_CHECKTYPESフラグを設定する必要がありMyTypeます。

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*/
    ...
};

このフラグを設定すると、インタープリターは型をチェックしないため、手動で処理する必要があります。

代わりに、インプレース演算子は常に異なる型を許可します。それはなぜですか、わかりません。

于 2012-09-16T18:09:40.410 に答える