3

noddy.Noddyメソッドで型を定義する python (2.7.5) 拡張機能を作成する__radd__と、(それ以外は同等の) カスタムの python defined-class オブジェクトとは異なる動作になります__radd__(前者は機能しませんが、後者は機能します)。例:

class PythonClass():
    def __radd__(self, other):
        return 'indeed!'

w = PythonClass()
d = noddy.Noddy()

print(w.__radd__)
print(d.__radd__)

print('the following works:')
print([1] + w)
print('the following does not work:')
print([1] + d)

対応する出力:

<bound method PythonClass.__radd__ of <__main__.PythonClass instance at 0xf6e9792c>>
<built-in method __radd__ of noddy.Noddy object at 0xf749d4b8>
the following works:
indeed!
the following does not work:
Traceback (most recent call last):
  File "examples/2.py", line 44, in <module>
    print([1] + d)
TypeError: can only concatenate list (not "noddy.Noddy") to list

メソッドd.__radd__は呼び出されませんが、呼び出されw.__radd__ます。なぜそうなのかについてのアイデアはありますか?[1] + xwhere xis a instanceの動作はドキュメンテーションPythonClassと一致しているようで、同様に機能することを期待しています。また、どちらも とは関係のない型です。noddy.Noddylist

回避策は大歓迎です。私はすでにこの問題を私の親しい友人である著者の注意を喚起しましたが、 で パッチを適用しようとしましたが、成功しませんでしlist.__radd__

編集

...そして、これが C ランドの写真です。

typedef struct {
    PyObject_HEAD
} Noddy;


static PyObject*
Noddy_radd(PyObject* _self, PyObject* args) {
    printf("Noddy_radd!\n");
    return NULL;
}

static PyObject*
Noddy_add(PyObject* _self, PyObject* args) {
  printf("Noddy_add\n");
  return NULL;
}

PyNumberMethods noddy_nums = {
  Noddy_add,         /* binaryfunc nb_add;         /* __add__ */
    0,               /* binaryfunc nb_subtract;    /* __sub__ */
    0,               /* binaryfunc nb_multiply;    /* __mul__ */
    0,               /* binaryfunc nb_divide;      /* __div__ */
    0,               /* binaryfunc nb_remainder;   /* __mod__ */
    0,               /* binaryfunc nb_divmod;      /* __divmod__ */
    0,               /* ternaryfunc nb_power;      /* __pow__ */
    0,               /* unaryfunc nb_negative;     /* __neg__ */
    0,               /* unaryfunc nb_positive;     /* __pos__ */
    0,               /* unaryfunc nb_absolute;     /* __abs__ */
    0,               /* inquiry nb_nonzero;        /* __nonzero__ */
    0,               /* unaryfunc nb_invert;       /* __invert__ */
    0,               /* binaryfunc nb_lshift;      /* __lshift__ */
    0,               /* binaryfunc nb_rshift;      /* __rshift__ */
    0,               /* binaryfunc nb_and;         /* __and__ */
    0,               /* binaryfunc nb_xor;         /* __xor__ */
    0,               /* binaryfunc nb_or;          /* __or__ */
    0,               /* coercion nb_coerce;        /* __coerce__ */
    0,               /* unaryfunc nb_int;          /* __int__ */
    0,               /* unaryfunc nb_long;         /* __long__ */
    0,               /* unaryfunc nb_float;        /* __float__ */
    0,               /* unaryfunc nb_oct;          /* __oct__ */
    0,               /* unaryfunc nb_hex;          /* __hex__ */
};

static PyMethodDef Noddy_methods[] = {
    {"__radd__", (PyCFunction)Noddy_radd, METH_VARARGS,
     "__radd__ function"},
    {NULL}  /* Sentinel */
};


static PyTypeObject NoddyType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "noddy.Noddy",             /*tp_name*/
    sizeof(Noddy),             /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    0,                         /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    &noddy_nums,               /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT |
      Py_TPFLAGS_HAVE_SEQUENCE_IN | /* tp_flags */
      Py_TPFLAGS_HAVE_ITER,
    "Noddy objects",           /* tp_doc */
    0,                     /* tp_traverse */
    0,                     /* tp_clear */
    0,                     /* tp_richcompare */
    0,                     /* tp_weaklistoffset */
    0,                     /* tp_iter */
    0,                     /* tp_iternext */
    Noddy_methods,             /* tp_methods */
    0,                         /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    0,                         /* tp_init */
    0,                         /* tp_alloc */
    PyType_GenericNew,         /* tp_new */
};
4

3 に答える 3

1

Python の getattr はトリッキーです。この__radd__方法は[悪名高い]魔法の方法の一部です. ob_type->tp_methodsこれらは、通常のメソッド ( )と同じ配列には格納されず、 Number Protocolob_type->tp_as_numberによって個別に管理される の一部です。

禁断の果実には、これらのメソッドにモンキー パッチを適用する機能を要求する問題があります。この取り組みはここに文書化されています

于 2013-09-14T18:57:11.960 に答える
0

どのように実装しました__radd__か?

C 拡張機能の場合、__radd__明示的に定義されていません。nb_add2 つの引数を受け取る関数へのポインターをサポートする、呼び出される 1 つのスロットがあります。Python クラス メソッドでは、最初の引数は常にインスタンス (つまりself) です。したがって、通常の方法と反映された方法の両方が必要です。これは、C 拡張機能には当てはまりません。nb_addいずれかの引数としてインスタンスで呼び出すことができます。

編集

Noddy_add の署名を のように書き換えるとわかりやすいでしょうNoddy_add(PyObject* a, PyObject* b)。d をカスタム C 型のインスタンスとします。次に[1] + d、次のように処理されます (構文の乱用やいくつかの特殊なケースは無視してください)。

PyNumber_Add([1], d)と呼ばれます。ListType.nb_add([1], d)ListType が を実装していないため、最初にどちらが失敗するかを試みますnb_add。次に試行NoddyType.nb_add([1], d)し、これが処理したい呼び出しです。この呼び出しが失敗すると、ListType.sq_concat([1], d)が呼び出されます。

を評価するd + [1]と、同じシーケンスが で終了しNoddyType.nb_add(d, [1])ます。ListType.sq_concatNoddyType のシーケンス メソッドを実装しない限り、呼び出されません。

Noddy_addリストへの参照を最初のパラメータとして、NoddyType への参照を 2 番目のパラメータとして呼び出すことができるように変更する必要があります。引数を逆にして呼び出すことは、Python コードnb_addと同等です。__radd__

詳細については、PyNumber_AddObjects/abstract.c を参照してください。

于 2013-09-13T23:00:20.200 に答える
0

Python Docsからフッターを見てください。

同じ型のオペランドの場合、反映されていないメソッド ( add () など) が失敗した場合、操作はサポートされていないと見なされます。これが、反映されたメソッドが呼び出されない理由です。

于 2013-09-13T20:01:21.960 に答える