2012-07-01 56 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