5

说有:

class A(B):
    ...

哪里B可能object...不是:_

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

我该怎么做才A.c()不会触发呼叫AttributeError

换句话说,我知道可以在运行时手动将类方法添加到类中。但是是否可以自动执行此操作,例如每次缺少类方法时都会创建一些虚拟方法?

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

4

3 回答 3

14

您可以通过在元类上使用__getattr__钩子来实现这一点。

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调用结果直接设置到类上,有效地将其缓存以供将来查找。

如果您需要在每次调用时重新生成类方法,请使用相同的方法将函数绑定到实例,但使用类和元类(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装饰器或描述符协议。

于 2012-07-01T11:56:03.590 回答
1

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

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

(setattr、getattr 的技巧是让 Python 做函数 -> 方法转换,而不需要弄乱它)

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
于 2012-07-01T12:29:12.953 回答
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'
于 2012-07-01T12:03:45.080 回答