2009-12-01 31 views
4

好吧,我會承認,這是一個巨大的混亂,我可以更好地實現它。這只是病態的好奇心,這促使我發現如何做到這一點。在運行時使用函數重載一個方法

class SomeClass(object): 
    def __init__(self): 
     def __(self, arg): 
      self.doStuff(arg) 
     self.overLoaded = __ 
    def doStuff(self, string): 
     print string 

SomeClass().overLoaded("test string") 

這將返回一個參數錯誤,因爲我只提供overLoaded()而不是兩個參數。是否有一些魔術可以告訴解釋者它現在是一個類的方法(我試着用@classmethod來裝飾它,我總是明白這是它的目的??)

+0

事實證明,'classmethod'在Python中的特定含義,所以我不認爲你的意思是類方法在您的標題。也許是重新組合,例如「用運行時綁定的函數替換方法」? –

+0

完成:) 感謝您指出。 – richo

回答

8

不要擔心自我參數,該函數已經從本地範圍中得到了。

class SomeClass(object): 
    def __init__(self): 
     def __(arg): 
      self.bar(arg) 
     self.foo = __ 
    def foo(self, arg): 
     print "foo", arg 
    def bar(self, arg): 
     print "bar", arg 

SomeClass().foo("thing") # prints "bar thing" 

當創建一個實例(後__new__,IIRC,但__init__之前)的Python結合所有的方法,自動將提供實例作爲第一個參數。如果您稍後添加方法,則需要手動提供實例。由於您正在定義已在範圍內的函數self,因此您無需再次傳遞該函數。

Python的new模塊不是解決方案,因爲它自2.6開始已被棄用。如果你想創建一個「真實」的實例方法,請使用像這樣的部分修飾器:

import functools 

class SomeClass(object): 
    def __init__(self): 
     def __(self, arg): 
      self.bar(arg) 
     self.foo = functools.partial(__, self) 
    def foo(self, arg): 
     print "foo", arg 
    def bar(self, arg): 
     print "bar", arg 

SomeClass().foo("thing") # prints "bar thing" 
+0

那是一些令人敬畏的魔法。謝謝! – richo

+0

如果要插入的函數是在類init外部定義的,該怎麼辦?在這種情況下,自我並沒有納入範圍。我總是忘記如何處理這種情況,因爲你基本上將一個未綁定的方法分配給一個實例,它不會自動綁定它... –

+0

sj26用partial()調用覆蓋他的第二節。 – richo

3

問題是你試圖添加一個新的實例方法(不是類方法),它沒有正確綁定。 Python有一個模塊函數可以將函數手動綁定到實例。

import new 
self.method = new.instancemethod(func, self, class) 

編輯:顯然模塊已被棄用。使用types模塊代替metamagic。

import types 
self.method = types.MethodType(func, self, class) 
0

sj26的解決方案是一個很好的解決方案。另一種方法是,如果您想要設置一個可以用任何用戶提供的函數或另一個對象的方法重載的方法,那麼就構建一個自定義描述符。這個描述符可以用作裝飾器(類似於@classmethod或@staticmethod);它允許你存儲在一個實例的字典功能,並將它作爲一個方法:

import types 

class overloadable(object): 
    def __init__(self, func): 
     self._default_func = func 
     self._name = func.__name__ 

    def __get__(self, obj, type=None): 
     func = obj.__dict__.get(self._name, self._default_func) 
     return types.MethodType(func, obj, type) 

    def __set__(self, obj, value): 
     if hasattr(value, 'im_func'): value = value.im_func 
     obj.__dict__[self._name] = value 

    def __delete__(self, obj): 
     del obj.__dict__[self._name] 

現在我們可以只裝飾的功能與「@overloadable」:

class SomeClass(object): 
    def doStuff(self, string): 
     print 'do stuff:', string 
    @overloadable 
    def overLoaded(self, arg): 
     print 'default behavior:', arg 

而且這會只是做的時候,我們重載它對於給定的情況下正確的事情:

>>> sc = SomeClass() 
>>> sc.overLoaded("test string") # Before customization 
default behavior: test string 

>>> sc.overLoaded = sc.doStuff  # Customize 
>>> sc.overLoaded("test string") 
do stuff: test string 

>>> del sc.overLoaded    # Revert to default behavior 
>>> sc.overLoaded("test string") 
default behavior: test string 
相關問題