正确覆盖 cython 扩展中的 __rmul__

Correct override of __rmul__ in cython Extensions

如何在 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 的相同实现有什么问题。

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

Arithmetic operator methods, such as __add__(), behave differently from their Python counterparts. There are no separate “reversed” versions of these methods (__radd__(), etc.) Instead, if the first operand cannot perform the operation, the same method of the second operand is called, with the operands in the same order.

This means that you can’t rely on the first parameter of these methods being “self” or being the right type, and you should test the types of both operands before deciding what to do. If you can’t handle the combination of types you’ve been given, you should return 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 class 的使用过于乐观,您可能还需要检查 other 的类型,and/or 检查更多通用 数量 类型。

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