1

I am trying to abstract one class (like shown here).

But when trying to call a method implemented only in the child, I get an error (pretty much the same as in this other question).

Here is an example:

# compile with:
# python setup.py build_ext --inplace

cdef class MetaB:
    cdef meta_method(self):
        print("called c_meta_method")

cdef class B(MetaB):
    cdef method(self):
        print("Called method")

cdef class A:
    cdef:
        MetaB b

    def __init__(self):
        self.b = B()

    cdef c_call_b_method(self):
        self.b.method()
    
    def call_method(self):
        return self.c_call_b_method()

    cdef c_call_b_meta_method(self):
        self.b.meta_method()
    
    def call_meta_method(self):
        return self.c_call_b_meta_method()

When I try it, the parent class method works fine:

$ python -c "import A; A.A().call_meta_method()"
[out]: called c_meta_method

But the child method does not work:

$ python -c "import A; A.A().call_method()"
[out]: Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "A.pyx", line 24, in A.A.call_method
    return self.c_call_b_method()
  File "A.pyx", line 21, in A.A.c_call_b_method
    self.b.method()
AttributeError: 'A.B' object has no attribute 'method'

How can I abstract a class, but still allow the child to expose different methods?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
zeh
  • 1,197
  • 2
  • 14
  • 29
  • Have you tried `( self.b).method()`? – Mechanic Pig Aug 23 '22 at 05:12
  • `MetaB` is pretty confusing here because it isn't a metaclass (and cdef classes don't support metaclasses) – DavidW Aug 23 '22 at 05:25
  • @DavidW , could you elaborate on the "cdef classes don't support metaclasses" ? – zeh Aug 23 '22 at 06:29
  • @MechanicPig, type casting works as expected. Thanks for the suggestion, but if I knew the type "B" before hand, I would change it from "MetaB" to "B" instead in the class A definition. Would there be a way to typecast dynamically ? (this sounds strange for cython, though >.<) – zeh Aug 23 '22 at 06:39
  • 2
    @zeh `cdef` defines C functions. Cython will not put them in the namespace of the class. Changing it to `def` should work. – Mechanic Pig Aug 23 '22 at 06:42
  • Metas classes are detailed in https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python - note that a metaclass is not the same as a base class. cdef classes support inheritance (like you're doing) but not setting a metaclass. It's that simple. What you're doing is fine, I just think the name is misleading – DavidW Aug 23 '22 at 07:11

1 Answers1

0

Solution 1: B b instead of MetaB b

cdef class A:
    cdef:
        B b

We replaced MetaB b with B b. MetaB doesn't have the attribute .method which is why the error occurred.

Solution 2: def instead of cdef

cdef class B(MetaB):
    def method(self):
        print("Called method")

Define B.method using def instead cdef. This will cause Cython to add method as an attribute to the B type

SargeATM
  • 2,483
  • 14
  • 24
  • I understand why the error occurred, but the problem is that the type of b must be `MetaB`, as I would like to be able to add other implementations of MetaB. – zeh Aug 23 '22 at 06:26
  • Then MetaB needs to define the protocol (or static "duck" typing) for the child classes to implement. – SargeATM Aug 23 '22 at 07:09
  • @zeh I found another solution that allows you to keep `MetaB b` – SargeATM Aug 23 '22 at 08:34