Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cython Metaclass .pxd: How should I implement `__eq__()`?

I am trying to augment an existing python source with a cython .pxd, as Stefan Behnel illustrates in slides 32 to 35 of "Using the Cython Compiler to write fast Python code".

As part of the exercise, I keep hitting a wall with the __eq__() method in my metaclass. I wish I could choose a simpler case to start Cython, but my production code isn't that simple. I cooked up a "minimal, complete example" to illustrate the problem... see the code at the bottom of the question.

Short story...

  • If I use cdef inline __richcmp__(Japan_Car_ABC self, Japan_Car_ABC other, int op):, cython complains that Special methods must be declared with 'def', not 'cdef'
  • If I use def __richcmp__(Japan_Car_ABC self, Japan_Car_ABC other, int op):, cython complains that function definition in pxd file must be declared 'cdef inline'

So cython gives me confusing guidance...

Questions:

  • I know that pure-python .pxd files have restrictions; is defining __richcmp__() in my .pxd a valid way to use a .pxd to augment pure python?
  • If this is a valid way to use a .pxd, how can I fix this to compile correctly?
  • If this is wrong, can a .pxd augment my pure python metaclass, without re-writing the whole metaclass in a .pyx file? (example: class Card in this project)

This is my .pxd...

### File: car_abc.pxd
# cython: infer_types=True
cimport cython

cdef class Japan_Car_ABC:
    cpdef public char* model
    cpdef public char* color

    def __richcmp__(Japan_Car_ABC self, Japan_Car_ABC other, int op):
        """Ref: http://docs.cython.org/src/userguide/special_methods.html#rich-comparisons"""
        if op==2:
            # op==2 is __eq__() in pure python
            if self.model==other.model:
                return True
            return False
        else:
            err_msg = "op {0} isn't implemented yet".format(op)
            raise NotImplementedError(err_msg)

Informational:

car_abc.py:

### File: car_abc.py
from abc import ABCMeta

class Japan_Car_ABC(object):
    __metaclass__ = ABCMeta

    def __init__(self, model="", color=""):
        self.model = model
        self.color = color

    def __eq__(self, other):
        if self.model==other.model:
            return True
        return False

    def __hash__(self):
        return hash(self.model)

car.py:

from car_abc import Japan_Car_ABC

class Lexus(Japan_Car_ABC):
    def __init__(self, *args, **kwargs):
        bling = kwargs.pop("bling", True)     # bling keyword (default: True)
        super(Lexus, self).__init__(*args, **kwargs)
        self.bling = bling

class Toyota(Japan_Car_ABC):
    def __init__(self, *args, **kwargs):
        super(Toyota, self).__init__(*args, **kwargs)


if __name__=="__main__":
    gloria_car = Lexus(model="ES350", color="White")
    jeff_car = Toyota(model="Camry", color="Silver")
    print("gloria_car.model: {0}".format(gloria_car.model))
    print("jeff_car.model: {0}".format(jeff_car.model))
    print("gloria_car==jeff_car: {0}".format(gloria_car==jeff_car))

setup.py:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules = [Extension("car_abc", ["car_abc.py"]),
    #Extension("car", ["car.py"]),
    ]

setup(
    name = "really build this thing",
    cmdclass = {'build_ext': build_ext},
    ext_modules = ext_modules
)
like image 268
Mike Pennington Avatar asked Jan 19 '15 11:01

Mike Pennington


1 Answers

The simple answer (received from Nils Bruin via cython-users) is that a pxd cannot be used to implement methods (such as __richcmp__()). Since I'm using a metaclass, I need to port my code into a .pyx, so I can implement __richcmp__() and other special cython methods.

like image 75
Mike Pennington Avatar answered Sep 23 '22 15:09

Mike Pennington