说有:
class A(B):
...
其中B
可以是object
而...
不是:
@classmethod # or @staticmethod
def c(cls): print 'Hello from c!'
我必须做什么才能调用A.c()
不会触发AttributeError
?
换句话说,我知道可以在运行时手动将类方法添加到类中。但是有可能自动这样做,比如每次缺少类方法时都会创建一些虚拟方法吗?
换句话说,只有当我可以用处理A.__dict__
的dict替换__getitem__
- 但A.__dict__
似乎不可写...
答案 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'