Andrey Cizov
Andrey Cizov

Reputation: 703

Python overriding classmethod in children

I am writing a simplistic ORM that would require me to override the methods inside the child classes. Depending on the context, I am either expecting to access the model through the class methods, or through the instance methods, therefore I can not simply override them. I believe this code describes the question well enough:

class A:
    @classmethod
    def key_fn(cls, id):
        raise NotImplementedError('')

    @classmethod
    def load_all(cls):
        yield from db_fetch_prefix(cls.key_fn('')):

class B(A):
    @classmethod
    def key_fn(cls, id):
        return f'/keys/{id}'

# how do I make sure B.key_fn is called here?
B.load_all()

Upvotes: 6

Views: 13936

Answers (1)

dnswlt
dnswlt

Reputation: 3105

Your B.key_fn would be called indeed. But your load_all returns a generator, because you let it yield from db_fetch_prefix. You can check this by running print(B.load_all()) at the end. The output will be:

python .\clsss.py
<generator object A.load_all at 0x0521A330>

I don't know what you want to achieve by using yield from. But an example that shows overriding of classmethods in subclasses is possible would be:

class A:
    @classmethod
    def key_fn(cls, id):
        raise NotImplementedError('')

    @classmethod
    def load_all(cls):
        return cls.key_fn('foo')

class B(A):
    @classmethod
    def key_fn(cls, id):
        return f'/keys/{id}'

print(B.load_all())  # prints "/keys/foo"

Guessing wildly that you want to apply the key_fn to each item yielded by your generator db_fetch_prefix (abbreviated to fetch below), the code below shows B.key_fn will be used in the presence of generators as well.

def fetch(callback):
    for i in range(5):  # fake DB fetch
        yield callback(i)

class A:
    @classmethod
    def key_fn(cls, id):
        raise NotImplementedError('')

    @classmethod
    def load_all(cls):
        yield from fetch(cls.key_fn)

class B(A):
    @classmethod
    def key_fn(cls, id):
        return f'/keys/{id}'

print(list(B.load_all()))  # prints ['/keys/0', '/keys/1', '/keys/2', '/keys/3', '/keys/4']

Upvotes: 4

Related Questions