如果 __richcmp__ 仅部分实现,我们应该引发 TypeError 吗?
Should we raise TypeError if __richcmp__ only partially implemented?
我为 Cython 扩展类型 (cdef class
) 实现了方法 __richcmp__
。有些比较案例是未定义的(例如<
),所以我曾经为他们提出一个Exception
,as follows。
def __richcmp__(Function self, Function other, op):
if other is None:
eq = False
else:
# guard against mixing managers
assert self.manager == other.manager
eq = (self.node == other.node)
if op == 2:
return eq
elif op == 3:
return not eq
else:
raise TypeError('Only `__eq__` and `__ne__` defined.')
我想要 pprint
这个 Cython class 实例的容器。 pprint
attempts to compare them, except for a TypeError
. My understanding is that as TypeError
, pprint
anticipates the case of undefined __lt__
, or the case of different types of objects (see also Python docs).
但是,__richcmp__
是 实施的,因此 Python 不会引发 TypeError
。它调用 __richcmp__
,我提出一个 Exception
,pprint
并没有忽略它。 Cython requires 实现了 __richcmp__
,所以我没有选择只定义 __eq__
和 __ne__
.
我更改了代码以引发 TypeError
。似乎如果 Python 用 TypeError
传达 __lt__
的缺失,那么我应该做同样的事情,以表示 __lt__
不存在,尽管存在 __lt__
整个 __richcmp__
,这是使用 Cython 的副产品,而不是设计意图。
这个推理有道理吗?我应该提出另一种例外吗?在这种情况下,我是否正确解释了 TypeError
的含义?
是的。 Cython 将您的实现用作 C API tp_richcompare
。该文件告诉你
If you want to implement a type for which only a limited set of comparisons makes sense (e.g. ==
and !=
, but not <
and friends), directly raise TypeError
in the rich comparison function.
这给了你一个相当强烈的暗示,表明这是正确的做法。
@DavidW 提供了非常准确和有用的答案,谢谢。它指出了如何处理类似的未来问题:通过查看生成的 C 代码。我将此答案发布为后代的免费信息。
在生成的 cudd.c
文件中搜索,我们找到 class 签名:
static PyTypeObject __pyx_type_2dd_4cudd_Function = {
PyVarObject_HEAD_INIT(0, 0)
"dd.cudd.Function", /*tp_name*/
sizeof(struct __pyx_obj_2dd_4cudd_Function), /*tp_basicsize*/
0, /*tp_itemsize*/
...
...
__pyx_pw_2dd_4cudd_8Function_13__richcmp__, /*tp_richcompare*/
...
...
#if PY_VERSION_HEX >= 0x030400a1
0, /*tp_finalize*/
#endif
};
(点与我们的讨论无关。) PyTypeObject
由 CPython C API 定义。函数__pyx_pw_2dd
后面定义
/* "dd/cudd.pyx":1556
* return Cudd_DagSize(self.node)
*
* def __richcmp__(Function self, Function other, op): # <<<<<<<<<<<<<<
* if other is None:
* eq = False
*/
/* Python wrapper */
static PyObject *__pyx_pw_2dd_4cudd_8Function_13__richcmp__(PyObject *__pyx_v_self, PyObject *__pyx_v_other, int __pyx_arg_op); /*proto*/
static PyObject *__pyx_pw_2dd_4cudd_8Function_13__richcmp__(PyObject *__pyx_v_self, PyObject *__pyx_v_other, int __pyx_arg_op) {
PyObject *__pyx_v_op = 0;
PyObject *__pyx_r = 0;
找到第一个摘录的方法是首先在 C 文件中搜索感兴趣的 Python 行(这里是 Function.__richcmp__
的签名),然后搜索 C 函数的调用者(虽然在这个讨论的指导下,我通过搜索 tp_richcompare
找到了它们)。
确认一下,我相信 Cython 生成了第一个摘录 Cython/Compiler/TypeSlots.py:
# Later -- synthesize a method to split into separate ops?
MethodSlot(richcmpfunc, "tp_richcompare", "__richcmp__", inherited=False), # Py3 checks for __hash__
有趣的是,那里的评论可能暗示将来用户将能够实现单独的比较器方法,从而不会直接遇到这个问题。
我为 Cython 扩展类型 (cdef class
) 实现了方法 __richcmp__
。有些比较案例是未定义的(例如<
),所以我曾经为他们提出一个Exception
,as follows。
def __richcmp__(Function self, Function other, op):
if other is None:
eq = False
else:
# guard against mixing managers
assert self.manager == other.manager
eq = (self.node == other.node)
if op == 2:
return eq
elif op == 3:
return not eq
else:
raise TypeError('Only `__eq__` and `__ne__` defined.')
我想要 pprint
这个 Cython class 实例的容器。 pprint
attempts to compare them, except for a TypeError
. My understanding is that as TypeError
, pprint
anticipates the case of undefined __lt__
, or the case of different types of objects (see also Python docs).
但是,__richcmp__
是 实施的,因此 Python 不会引发 TypeError
。它调用 __richcmp__
,我提出一个 Exception
,pprint
并没有忽略它。 Cython requires 实现了 __richcmp__
,所以我没有选择只定义 __eq__
和 __ne__
.
我更改了代码以引发 TypeError
。似乎如果 Python 用 TypeError
传达 __lt__
的缺失,那么我应该做同样的事情,以表示 __lt__
不存在,尽管存在 __lt__
整个 __richcmp__
,这是使用 Cython 的副产品,而不是设计意图。
这个推理有道理吗?我应该提出另一种例外吗?在这种情况下,我是否正确解释了 TypeError
的含义?
是的。 Cython 将您的实现用作 C API tp_richcompare
。该文件告诉你
If you want to implement a type for which only a limited set of comparisons makes sense (e.g.
==
and!=
, but not<
and friends), directly raiseTypeError
in the rich comparison function.
这给了你一个相当强烈的暗示,表明这是正确的做法。
@DavidW 提供了非常准确和有用的答案,谢谢。它指出了如何处理类似的未来问题:通过查看生成的 C 代码。我将此答案发布为后代的免费信息。
在生成的 cudd.c
文件中搜索,我们找到 class 签名:
static PyTypeObject __pyx_type_2dd_4cudd_Function = {
PyVarObject_HEAD_INIT(0, 0)
"dd.cudd.Function", /*tp_name*/
sizeof(struct __pyx_obj_2dd_4cudd_Function), /*tp_basicsize*/
0, /*tp_itemsize*/
...
...
__pyx_pw_2dd_4cudd_8Function_13__richcmp__, /*tp_richcompare*/
...
...
#if PY_VERSION_HEX >= 0x030400a1
0, /*tp_finalize*/
#endif
};
(点与我们的讨论无关。) PyTypeObject
由 CPython C API 定义。函数__pyx_pw_2dd
后面定义
/* "dd/cudd.pyx":1556
* return Cudd_DagSize(self.node)
*
* def __richcmp__(Function self, Function other, op): # <<<<<<<<<<<<<<
* if other is None:
* eq = False
*/
/* Python wrapper */
static PyObject *__pyx_pw_2dd_4cudd_8Function_13__richcmp__(PyObject *__pyx_v_self, PyObject *__pyx_v_other, int __pyx_arg_op); /*proto*/
static PyObject *__pyx_pw_2dd_4cudd_8Function_13__richcmp__(PyObject *__pyx_v_self, PyObject *__pyx_v_other, int __pyx_arg_op) {
PyObject *__pyx_v_op = 0;
PyObject *__pyx_r = 0;
找到第一个摘录的方法是首先在 C 文件中搜索感兴趣的 Python 行(这里是 Function.__richcmp__
的签名),然后搜索 C 函数的调用者(虽然在这个讨论的指导下,我通过搜索 tp_richcompare
找到了它们)。
确认一下,我相信 Cython 生成了第一个摘录 Cython/Compiler/TypeSlots.py:
# Later -- synthesize a method to split into separate ops?
MethodSlot(richcmpfunc, "tp_richcompare", "__richcmp__", inherited=False), # Py3 checks for __hash__
有趣的是,那里的评论可能暗示将来用户将能够实现单独的比较器方法,从而不会直接遇到这个问题。