3

My application needs to have several Cython cdef classes that inherit from a single base class, but yet still implement many interfaces. These interfaces would be used to do isinstance() checks on the classes to make sure they conform to certain interfaces.

I know Cython doesn't support multiple inheritance, but is there any way at all to implement interface-like behavior. It seems like a rather glaring limitation in Cython and I'm sure I'm not the only person to encounter this problem.

hpaulj
  • 221,503
  • 14
  • 230
  • 353
Timothy Baldridge
  • 10,455
  • 1
  • 44
  • 80
  • Can you give an example of such interfaces in your Python code? Normally people start with Python code and port performance critical parts to `cython`. Or use `cython` as glue between Python code and `c` or `c++` code. – hpaulj Dec 26 '16 at 06:37

1 Answers1

5

You do it exactly as you would in pure Python when faced with a base class that you can't change but that you want to associate with an interface: you use the abstract base classes module

There's two options to chose from:

  1. you either register your class as belonging to an interface ("abstract base class") like I've done for InterfaceA, or
  2. you give your interface a __subclasshook__ that allows it to claim any class with the right methods like I've done for InterfaceB.

Example:

import abc

# define the interfaces as normal (non-Cython) Python classes
class InterfaceA(metaclass=abc.ABCMeta):
    # Python3 syntax. metaclasses are slightly different in python2   
    pass

class InterfaceB(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def useful_function(self):
        raise NotImplementedError()

    @classmethod
    def __subclasshook__(cls,other_cls):
        if cls is InterfaceB:
            if any("useful_function" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented


# our Cython class
cdef class C:
    def useful_function(self):
        return 1

c = C()

print(isinstance(c,InterfaceA)) # prints False
InterfaceA.register(C)
print(isinstance(c,InterfaceA)) # prints True
print(isinstance(c,InterfaceB)) # prints True
DavidW
  • 29,336
  • 6
  • 55
  • 86
  • Ah, I see, we're bailing out into pure python to do our inheritance. That works, and it's a bit more elegant than the hack I was thinking up. – Timothy Baldridge Dec 26 '16 at 17:02
  • 1
    It's not quite that: `class C` is still a Cython class and doesn't inherit from classes `InterfaceA` and `InterfaceB`. However `InterfaceA` and `InterfaceB` use pure Python to modify the behaviour of `isinstance` when it's called on them. – DavidW Dec 26 '16 at 19:24
  • See also this excellent answer, which also mentions multiple inheritance: https://stackoverflow.com/a/3392406/1959808 – 0 _ Sep 09 '17 at 06:47