在Cython扩展中正确覆盖__rmul__方法

4
我该如何在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实现有什么问题。

2个回答

2
这是一个未阅读文档的案例。在扩展类型的特殊方法用户指南中,您会发现以下内容:
算术运算符方法(如__add__())与它们的Python同伴行为不同。这些方法没有单独的“reversed”版本(__radd__()等)。相反,如果第一个操作数不能执行操作,则调用第二个操作数的相同方法,操作数顺序相同。
这意味着您不能保证这些方法的第一个参数是“self”或正确的类型,并且您应该测试两个操作数的类型,然后再决定要做什么。如果您无法处理给定的类型组合,则应返回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的类型,以及/或者检查更通用的数字类型。


谢谢!是的,我有罪。我没有查看文档,而是在谷歌上搜索了很多(通常情况下,当问题是某些必须工作的东西时,我在那里得到了更好的答案)。我很抱歉,再次感谢。 - diego

0
Cython 3 alpha版本现在应该支持与Python行为相同的__rmul__。因此升级到该版本将使您的代码按原样工作(尽管在撰写问题时这并不是一个选项)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接