Search code examples
pythonpython-3.xlazy-evaluationclass-method

How to add a classmethod in Python dynamically


I'm using Python 3. I know about the @classmethod decorator. Also, I know that classmethods can be called from instances.

class HappyClass(object):
    @classmethod
    def say_hello():
        print('hello')
HappyClass.say_hello() # hello
HappyClass().say_hello() # hello

However, I don't seem to be able to create class methods dynamically AND let them be called from instances. Let's say I want something like

class SadClass(object):
    def __init__(self, *args, **kwargs):
        # create a class method say_dynamic

SadClass.say_dynamic() # prints "dynamic!"
SadClass().say_dynamic() # prints "dynamic!"

I've played with cls.__dict__ (which produces exceptions), and with setattr(cls, 'say_dynamic', blahblah) (which only makes the thingie callable from the class and not the instance).

If you ask me why, I wanted to make a lazy class property. But it cannot be called from instances.

@classmethod
def search_url(cls):
    if  hasattr(cls, '_search_url'):
        setattr(cls, '_search_url', reverse('%s-search' % cls._meta.model_name))
    return cls._search_url

Maybe because the property hasn't been called from the class yet...

In summary, I want to add a lazy, class method that can be called from the instance... Can this be achieved in an elegant (nottoomanylines) way?

Any thoughts?

How I achieved it

Sorry, my examples were very bad ones :\

Anyway, in the end I did it like this...

@classmethod
def search_url(cls):
    if not hasattr(cls, '_search_url'):
        setattr(cls, '_search_url', reverse('%s-search' % cls._meta.model_name))
    return cls._search_url

And the setattr does work, but I had made a mistake when testing it...


Solution

  • How I achieved it:

    @classmethod
    def search_url(cls):
        if not hasattr(cls, '_search_url'):
            setattr(cls, '_search_url', reverse('%s-search' % cls._meta.model_name))
        return cls._search_url