2012-07-01 59 views
4

说有:Python的动态类方法

class A(B): 
    ... 

其中B可能是object...

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

我有什么做的,调用A.c()不会触发AttributeError

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

在另一也就是说,只有当我可以取代A.__dict__我的字典它处理__getitem__ - 但A.__dict__似乎是不可写......

回答

11

您可以通过在metaclass使用__getattr__ hook实现这一目标。

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

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

+0

A.asd() - AttributeError的:“对象类型 'A' 具有没有属性'asd'“ –

+0

@EcirHana:啊,你的意思是一个默认的类方法,将会更新。 –

+0

我希望垃圾邮件是类方法,而不是实例方法。 –

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

或者您可以使用somethigs这样的:

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' 
+0

谢谢。是的,我知道你可以手动添加类方法。有没有办法自动做到这一点? –

+0

@EcirHana,python在模块导入时实例化类对象。而这个类对象没有方法'__getattribute__'或一些等价的... – astynax

+0

@EcirHana,我试图实现某种包装... – astynax

1

由像__getattr__和描述符协议方法提供给实例可以为班级工作为好,但在这种情况下的行为,你必须将它们编码到类的元类中。

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

(该SETATTR,GETATTR窍门是让Python的做功能 - >方法transoform,无需惹它)

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