4

我想猴子補丁一個單一的classmethod,保持舊的功能。考慮我的代碼來獲得這個想法。這是我的代碼(非常合成的例子)。如何包裝(猴子補丁)@classmethod

#!/usr/bin/env python 

class A: 

    @classmethod 
    def foo(kls, param): 
    print 'A.foo called, param is ' + param 

    def bar(self, param): 
    print 'A.bar called, param is ' + param 


a = A() 
a.foo('param_foo') 
a.bar('param_bar') 

# Patching things 

def bar_wrapper(wrapped_func): 
    def _w(*args, **kwargs): 
    print '<bar_wrap>' 
    wrapped_func(*args, **kwargs) 
    print '</bar_wrap>' 
    return _w 

def foo_wrapper(wrapped_func): 
    # Something missing here? 
    def _w(*args, **kwargs): 
    print '<foo_wrap>' 
    wrapped_func(*args, **kwargs) 
    print '</foo_wrap>' 
    return _w 

# Everything is pretty ok 
A.bar = bar_wrapper(A.bar) 
a.bar('is_is_wrapped?') 

# Failed to wrap @classmethod 
A.foo = foo_wrapper(A.foo) 
A.foo('another_wrap_test') 

這是我所希望輸出:

A.foo called, param is param_foo 
A.bar called, param is param_bar 
<bar_wrap> 
A.bar called, param is is_is_wrapped? 
</bar_wrap> 
<foo_wrap> 
A.foo called, param is another_wrap_test 
</foo_wrap> 

而這就是我得到:

A.foo called, param is param_foo 
A.bar called, param is param_bar 
<bar_wrap> 
A.bar called, param is is_is_wrapped? 
</bar_wrap> 
Traceback (most recent call last): 
    File "./pytest.py", line 39, in <module> 
    A.foo('another_wrap_test') 
TypeError: unbound method _w() must be called with A instance as first argument (got str instance instead) 

好像一個參數(類PARAM)包裝過程中丟失。或者我不想裝飾功能?

在此先感謝。

+2

'A.foo =靜態方法(foo_wrapper(A.foo))' – falsetru

回答

6

當你訪問一個類的方法時,它在當時被包裝;方法在這裏充當descriptors

您可能需要再次解開的方法,返回一個包裹包裝:

def foo_wrapper(wrapped_func): 
    wrapped_func = wrapped_func.__func__ 
    def _w(*args, **kwargs): 
     print '<foo_wrap>' 
     wrapped_func(*args, **kwargs) 
     print '</foo_wrap>' 
    return classmethod(_w) 

現在返回的裝飾本身就是一個類的方法,以及包裝工作:

>>> class A: 
...  @classmethod 
...  def foo(kls, param): 
...   print 'A.foo called, param is ' + param 
... 
>>> def foo_wrapper(wrapped_func): 
...  wrapped_func = wrapped_func.__func__ 
...  def _w(*args, **kwargs): 
...   print '<foo_wrap>' 
...   wrapped_func(*args, **kwargs) 
...   print '</foo_wrap>' 
...  return classmethod(_w) 
... 
>>> A.foo = foo_wrapper(A.foo) 
>>> A.foo('bar') 
<foo_wrap> 
A.foo called, param is bar 
</foo_wrap>