2016-09-12 7 views
0

クラスメソッドのラッパーを作成し、特定のメソッドの呼び出しの前および/または後に実行する必要があります。クラスメソッドのデコレータ: 'getattr'との互換性

class MyClass: 

    def call(self, name): 
     print "Executing function:", name 
     getattr(self, name)() 

    def my_decorator(some_function): 
     def wrapper(): 
      print("Before we call the function.") 
      some_function() 
      print("After we call the function.") 
      return wrapper 

    @my_decorator 
    def my_function(self): 
     print "My function is called here." 


engine = MyClass() 
engine.call('my_function') 

これはラインgetattr(self, name)()で私にエラーを与える:私はクラスメソッドの前にデコレータをコメントアウトした場合

TypeError: 'NoneType' object is not callable

が、それは完璧に動作します:ここで

は、最小限の例です

class MyClass: 

    def call(self, name): 
     print "Executing function:", name 
     getattr(self, name)() 

    def my_decorator(some_function): 
     def wrapper(): 
      print("Before we call the function.") 
      some_function() 
      print("After we call the function.") 
      return wrapper 

    # @my_decorator 
    def my_function(self): 
     print "My function is called here." 


engine = MyClass() 
engine.call('my_function') 

出力は、

です。

Executing function: my_function

My function is called here.

デコレータ自体は教科書の例と同じです。 getattrで装飾されたメソッドをPythonで呼び出すと、何かが間違っているように見えます。

このコードを修正する方法はありますか?

答えて

2

getattr()とは関係ありません。あなたが直接my_function()を呼び出すしようとすると、まったく同じエラーが出る:

  • あなたのデコレータはそうNoneが代わりに返され、wrapper返すことはありません:

    >>> engine.my_function() 
    Traceback (most recent call last): 
        File "<stdin>", line 1, in <module> 
    TypeError: 'NoneType' object is not callable 
    

    あなたは2つの問題を抱えています。この戻り値はmy_functionを置き換え、エラーの直接の原因です。 MyClass.my_functionNoneに設定されています:

    >>> MyClass.my_function is None 
    True 
    
  • あなたのラッパーがself含む引数を取りません。あなたがそれを適切に返すと、それが働くためにはこれが必要になります。

最初の問題は、return wrapper行のインデントを解除することで修正されました。それは現在wrapper機能自体の一部であり、代わりにmy_decoratorの一部である必要があります:あなたの質問は、部分的にしか答えた

def my_decorator(some_function): 
    def wrapper(self): 
     print("Before we call the function.") 
     # some_function is no longer bound, so pass in `self` explicitly 
     some_function(self) 
     print("After we call the function.") 
    # return the replacement function 
    return wrapper 
1

。ここでは、彼らは追加の引数 - それは(だけでなく、Pythonの2と3の両方で)完全に動作するようになりますを受け入れるようにwrapper(だけでなく、call())メソッドを変更する方法は次のとおりです。

class MyClass: 

    def call(self, name, *args, **kwargs): 
     print("Executing function: {!r}".format(name)) 
     getattr(self, name)(*args, **kwargs) 

    def my_decorator(some_function): 
     def wrapper(self, *args, **kwargs): 
      print("Before we call the function.") 
      retval = some_function(self, *args, **kwargs) 
      print("After we call the function.") 
      return retval 
     return wrapper 

    @my_decorator 
    def my_function(self): 
     print("My function is called here.") 

    del my_decorator # Not a permanent part of class. 


engine = MyClass() 
engine.call('my_function') 

出力:

Executing function: 'my_function' 
Before we call the function. 
My function is called here. 
After we call the function.