Search code examples
cython

using special function such as __add__ in cython cdef classes


I am wanting to create a cython object that can has convenient operations such as addition multiplication and comparisons. But when I compile such classes they all seem to have a lot of python overhead.

A simple example:

%%cython -a

cdef class Pair:
    cdef public:
        int a
        int b
        
    def __init__(self, int a, int b):
        self.a = a
        self.b = b
        
    def __add__(self, Pair other):
        return Pair(self.a + other.a, self.b + other.b)

p1 = Pair(1, 2)
p2 = Pair(3, 4)

p3 = p1+p2

print(p3.a, p3.b)

But I end up getting quite large readouts from the annotated compiler It seems like the __add__ function is converting objects from python floats to cython doubles and doing a bunch of type checking. Am I doing something wrong?


Solution

  • There's likely a couple of issues:

    1. I'm assuming that you're using Cython 0.29.x (and not the newer Cython 3 alpha). See https://cython.readthedocs.io/en/stable/src/userguide/special_methods.html#arithmetic-methods

      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

      It is likely treating self as untyped and thus accessing a and b as Python attributes.

      The Cython 3 alpha treats special methods differently (see https://cython.readthedocs.io/en/latest/src/userguide/special_methods.html#arithmetic-methods) so you could also consider upgrading to that.

    2. Although the call to __init__ has C typed arguements it's still a Python call so you can't avoid boxing and unboxing the arguments to Python ints. You could avoid this call and do something like:

      cdef Pair res = Pair.__new__()
      res.a = ... # direct assignment to attribute