Python动态类方法

时间:2012-07-01 11:48:21

标签: python runtime class-method

说有:

class A(B):
    ...

其中B可以是object... 不是

@classmethod # or @staticmethod
def c(cls): print 'Hello from c!'

我必须做什么才能调用A.c()不会触发AttributeError

换句话说,我知道可以在运行时手动将类方法添加到类中。但是有可能自动这样做,比如每次缺少类方法时都会创建一些虚拟方法吗?

换句话说,只有当我可以用处理A.__dict__的dict替换__getitem__ - 但A.__dict__似乎不可写...

3 个答案:

答案 0 :(得分:12)

您可以使用__getattr__ hook上的metaclass来实现此目的。

class DefaultClassMethods(type):
    def __getattr__(cls, attr):
        def _defaultClassMethod(cls):
            print 'Hi, I am the default class method!'
        setattr(cls, attr, classmethod(_defaultClassMethod))
        return getattr(cls, attr)

演示:

>>> class DefaultClassMethods(type):
...     def __getattr__(cls, attr):
...         def _defaultClassMethod(cls):
...             print 'Hi, I am the default class method!'
...         setattr(cls, attr, classmethod(_defaultClassMethod))
...         return getattr(cls, attr)
... 
>>> class A(object):
...     __metaclass__ = DefaultClassMethods
... 
>>> A.spam
<bound method DefaultClassMethods._defaultClassMethod of <class '__main__.A'>>
>>> A.spam()
Hi, I am the default class method!

请注意,我们将classmethod调用的结果直接设置到类上,有效地将其缓存以供将来查找。

如果需要在每次调用时重新生成类方法,请使用相同的method to bind a function to an instance,但使用类和元类(使用cls.__metaclass__与元类子类化一致):

from types import MethodType

class DefaultClassMethods(type):
    def __getattr__(cls, attr):
        def _defaultClassMethod(cls):
            print 'Hi, I am the default class method!'
        return _defaultClassMethod.__get__(cls, cls.__metaclass__)

对于静态方法,只需在所有情况下直接返回函数,不需要使用staticmethod装饰器或描述符协议。

答案 1 :(得分:1)

通过__getattr__等方法和描述符协议提供给实例的行为也适用于类,但在这种情况下,您必须在类的元类中对它们进行编码。

在这种情况下,所有人需要做的是将元类__getattr__函数设置为自动生成所需的类属性。

(setattr,getattr技巧是让Python执行函数&gt;方法转换而不需要乱用它)

class AutoClassMethod(type):
    def __getattr__(cls, attr):
        default = classmethod(lambda cls: "Default class method for " + repr(cls))
        setattr(cls, attr, default)
        return getattr(cls, attr)



class A(object):
    __metaclass__ = AutoClassMethod
    @classmethod
    def b(cls):
        print cls

答案 2 :(得分:0)

>>> class C(object):
...     pass
... 
>>> C.m = classmethod(lambda cls: cls.__name__)
>>> C.m()
'C'

或者您可以使用以下内容:

class Wrapper(object):

    def __init__(self, clz, default=lambda cls: None):
        self._clz = clz
        self._default = default

    def __getattr__(self, attr):
        # __attrs__ will be getted from Wrapper
        if attr.startswith('__'):
            return self.__getattribute__(attr)

        if not hasattr(self._clz, attr):
            setattr(self._clz, attr, classmethod(self._default))
        return getattr(self._clz, attr)

    def __call__(self, *args, **kwargs):
        return self._clz(*args, **kwargs)

>>> class C(object):
...     pass
...
>>> C = Wrapper(C, default=lambda cls: cls.__name__)
>>> c = C()
>>> print C.m()
'C'
>>> print c.m() # now instance have method "m"
'C'