如果__richcmp__仅部分实现,我们应该引发TypeError吗?

时间:2017-02-22 07:37:48

标签: python cython typeerror comparator

我为Cython扩展类型(__richcmp__)实现了方法cdef class。某些比较案例未定义(例如<),因此我曾经为它们as follows提出Exception

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类的实例容器。 pprint attempts to compare他们,TypeError除外。我的理解是,TypeErrorpprint预测未定义__lt__的情况,或不同类型对象的情况(另请参阅Python docs)。

但是,__richcmp__ 已实现,因此Python不会引发TypeError。它会调用__richcmp__,我会引发Exceptionpprint不会忽略它。 __richcmp__实施__eq__的Cython requires,因此我无法仅定义__ne__TypeError

我将代码更改为引发__lt__。似乎如果Python通过TypeError传达__lt__的缺失,那么我应该这样做,以表示__richcmp__不存在,尽管存在整个TypeError System.getProperty 1}},这是使用Cython的副产品,而不是设计意图。

这种推理有意义吗?我应该提出另一种例外吗?在这种情况下,我是否正确解释了System.getenv的含义?

2 个答案:

答案 0 :(得分:1)

是。 Cython将您的实现用作C API tp_richcompare。该文档告诉你

  

如果你想实现一种只有一组有限的比较有意义的类型(例如==!=,而不是<和朋友),直接提升{{1在丰富的比较函数中。

这给了你一个相当强烈的暗示,这是正确的事情。

答案 1 :(得分:0)

@DavidW提供了非常精确和有用的答案,谢谢。它指出了如何处理类似的未来问题:通过查看生成的C代码。我发布这个答案作为后人的免费信息。

在生成的cudd.c文件中搜索,我们找到了类签名:

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
};

(点的缩写与我们的讨论无关。)PyTypeObjectCPython 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__

有趣的是,那里的评论或许表明,未来用户将能够实施单独的比较方法,因此不会直接遇到这个问题。