How to add a class in Python dynamically

I am using Python 3. I know about the @classmethod decorator. In addition, 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 cannot dynamically create class methods and allow them to be called from instances. 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 played with cls.__dict__(which throws exceptions) and with setattr(cls, 'say_dynamic', blahblah)(which only makes the object called from the class, not the instance).

If you ask me why, I wanted to create 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

Perhaps because the property has not yet been called from a class ...

, , ... (nottoomanylines) ?

?

, :\

, ...

@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

setattr , ...

+4
4

:

@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
0

, , :

class SadClass:
    pass

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

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

, classmethod, , , . staticmethod ?

+7

, __init__, . :

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!"

, __init__ self , : , -

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

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

reset , , . , , , , SadClass.say_dynamic() , - , , , .

Also note that a classmethodreceives an implicit class argument cls; if you want your function to be called without any arguments, use a decorator staticmethod.

+3
source

As a side note, you can simply use the instance attribute to store the 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
0
source

Source: https://habr.com/ru/post/1546908/


All Articles