在cython Extensions中正确覆盖__rmul__

时间:2015-10-19 15:13:18

标签: python cython

我如何在cython中覆盖 rmul

例如,这在python中非常有效

class PyFoo:
  def __init__(self):
      self.a = 4
  def __mul__(self,b):  return b*self.a
  def __rmul__(self,b): return self*b

Pynew = PyFoo()

print "   Python   "
print Pynew*3 # I got 12
print 3*Pynew # I got 12

但是,如果我在Cython中实现相同功能,

cclass.pyx

cimport cython

cdef class Foo:
  cdef public int a
  def __init__(self):
      self.a = 4
  def __mul__(self,b):  return b*self.a
  def __rmul__(self,b): return self*b

test.py

import cclass as c
Cnew = c.Foo()
print "   Cython   "
print Cnew*3 # This works, I got 12
print 3*Cnew # This doesn't

我收到了这个错误

    Traceback (most recent call last):
  File "test.py", line 22, in <module>
    print 3*Cnew
  File "cclass.pyx", line 8, in cclass.Foo.__mul__ (cclass.c:763)
    def __mul__(self,b):  return b*self.a
AttributeError: 'int' object has no attribute 'a'

我不明白在Cython中使用 rmul 的相同实现有什么问题。

1 个答案:

答案 0 :(得分:1)

这是不阅读文档的情况。在Special Methods of Extension Types用户指南中,您会找到以下内容:

  

算术运算符方法(例如__add __())的行为方式不同   来自他们的Python同行。没有单独的“逆转”   这些方法的版本(__radd __()等)相反,如果是第一个   操作数不能执行操作,第二种方法相同   调用操作数,操作数的顺序相同。

     

这意味着您不能依赖这些方法的第一个参数   是“自我”或是正确的类型,你应该测试的类型   两个操作数在决定做什么之前。如果你不能处理   你已经给出的类型组合,你应该返回   NotImplemented。

所以你应该按照以下方式进行一些类型检查:

cdef class Foo:
    cdef public int a

    def __init__(self):
        self.a = 4

    def __mul__(first, other):
        if isinstance(first, Foo):
            return first.a * other
        elif isinstance(first, int):
            return first * other.a
        else:
            return NotImplemented

此解决方案对Foo类的使用过于乐观,您可能需要检查other的类型,和/或检查更通用的数字类型。