How to add a classmethod in Python dynamically

心不动则不痛 提交于 2019-12-04 05:19:49

You can add a function to a class at any point, a practice known as monkey-patching:

class SadClass:
    pass

@classmethod
def say_dynamic(cls):
    print('hello')
SadClass.say_dynamic = say_dynamic

>>> SadClass.say_dynamic()
hello
>>> SadClass().say_dynamic()
hello

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 use staticmethod 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:

class SadClass(object):
    pass

def say_dynamic(cls):
    print("dynamic")

SadClass.say_dynamic = classmethod(say_dynamic)
# or 
setattr(SadClass, 'say_dynamic', classmethod(say_dynamic))

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

Of course, in the __init__ method the self argument is an instance, and not the class: to put the method in the class there, you can hack something like

class SadClass(object):
    def __init__(self, *args, **kwargs):
        @classmethod
        def say_dynamic(cls):
            print("dynamic!")

        setattr(self.__class__, 'say_dynamic', say_dynamic)

But 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 argument cls; if you do want your function to be called without any arguments, use the staticmethod decorator.

As a side note, you can just use an instance attribute to hold a function:

>>> class Test:
...    pass
... 
>>> t=Test()
>>> t.monkey_patch=lambda s: print(s)
>>> t.monkey_patch('Hello from the monkey patch')
Hello from the monkey patch

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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!