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...
You can add a function to a class at any point, a practice known as monkey-patching:
Note that you are using the
classmethod
decorator, but your function accepts no arguments, which indicates that it's designed to be a static method. Did you mean to usestaticmethod
instead?If you want to create class methods, do not create them in the
__init__
function as it is then recreated for each instance creation. However, following works:Of course, in the
__init__
method theself
argument is an instance, and not the class: to put the method in the class there, you can hack something likeBut it will again reset the method for each instance creation, possibly needlessly. And notice that your code most probably fails because you are calling the
SadClass.say_dynamic()
before any instances are created, and thus before the class method is injected.Also, notice that a
classmethod
gets the implicit class argumentcls
; if you do want your function to be called without any arguments, use thestaticmethod
decorator.As a side note, you can just use an instance attribute to hold a function:
How I achieved it: