Search code examples
pythoncythongetattrgetattribute

Circumvent the non-standard behaviour of __getattr__ in a Cython extension class


I am writing code, which I want to work both with vanilla Python and as part of a Cythonized module. I have a cdef class that I want to behave normally, but on which I want to override the behaviour of __getattr__ (for undefined attributes). In Python this is simple, however Cython does not behave up to spec:

@cython.cclass
class Test:
    field: cython.declare(object, visibility='public')

    def __init__(self):
        self.field = 24

    def __getattr__(self, item):
        return 42

The expression Test().field, in Python, returns 24, as it should, and Test().anything_else gives 42. In Cython, Test().field returns 42 as __getattr__ is treated as if it was a __getattribute__. I do not want my object to have dynamic fields, I merely want to override the behaviour of the . operator for when a property is not found, as in vanilla Python.

I know I can define __getattr__ as:

def __getattr__(self, item):
    if item == 'field':
        return self.field
    return 42

But once you get a few properties this becomes tedious.

Is there a better way?


Solution

  • As noticed by @user2357112 in the comment my example contains a typo in the use of cython.declare, it should use = not ::

    @cython.cclass
    class Test:
        field = cython.declare(object, visibility='public')
    
        def __init__(self):
            self.field = 24
    
        def __getattr__(self, item):
            return 42
    

    This fixes the problem for me.