如何动态地在Python中添加classmethod

时间:2014-07-02 18:50:47

标签: python python-3.x lazy-evaluation class-method

我正在使用Python 3。 我知道@classmethod装饰器。另外,我知道可以从实例调用classmethods。

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

但是,我似乎无法动态创建类方法并让它们从实例中调用。假设我想要像

这样的东西
class SadClass(object):
    def __init__(self, *args, **kwargs):
        # create a class method say_dynamic

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

我使用cls.__dict__(产生异常)和setattr(cls, 'say_dynamic', blahblah)(只能从类中调用而不是实例)。

如果你问我为什么,我想制作一个懒惰的类属性。但它不能从实例中调用。

@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

也许是因为尚未从班级调用该属性......

总之,我想添加一个 lazy 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 个答案:

答案 0 :(得分:8)

你可以在任何时候向一个类添加一个函数,这种做法称为猴子修补:

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吗?

答案 1 :(得分:3)

如果要创建类方法,请不要在__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)

但它会再次为每个实例创建重置方法,可能是不必要的。请注意,您的代码很可能会失败,因为您在创建任何实例之前调用SadClass.say_dynamic(),因此在注入类方法之前。

另外,请注意classmethod获取隐式类参数cls;如果您确实希望在没有任何参数的情况下调用函数,请使用staticmethod装饰器。

答案 2 :(得分:0)

作为旁注,您可以使用实例属性来保存函数:

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

答案 3 :(得分:0)

我是如何实现的:

@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