当创建一个使用__radd__
方法定义noddy.Noddy
类型的python(2.7.5)扩展时,它将获得与具有自定义__radd__
(前者不起作用,后者起作用)的python定义的类对象不同的行为。示例:
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)
以及相应的输出:
^{pr2}$方法d.__radd__
不被调用,但w.__radd__
被调用。你知道为什么会这样吗?[1] + x
的行为,其中x
是一个PythonClass
实例,似乎与{a1}一致,我希望{list
无关。在
欢迎采取变通办法。我已经尝试过用forbiddenfruit修补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 */
};
您是如何实现
__radd__
?在对于C扩展,
__radd__
没有显式定义。有一个名为nb_add
的插槽,它支持指向接受两个参数的函数的指针。在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])
结尾。^除非实现noddype的sequence方法,否则不会调用{您需要修改
Noddy_add
,这样就可以在调用它时引用列表作为第一个参数,引用noddype作为第二个参数。用反转的参数调用nb_add
等同于Python代码中的__radd__
。在有关详细信息,请参见Objects/abstract.c中的
PyNumber_Add
从Python Docs看footer。在
Python的getattr是个狡猾的家伙。
__radd__
方法是著名魔术方法的一部分。它们与常规方法(ob_type->tp_methods
)不同,它是ob_type->tp_as_number
的一部分,由Number Protocol单独管理。在禁果有一个问题需要猴子修补这些方法。这项工作是documented here
相关问题 更多 >
编程相关推荐