5

私はPythonデコレータを初めて使用します。簡単な例を使って基本的な概念を理解しました。しかし、このより実用的なデコレータを読み込もうとすると、迷子になります。以下に、私の質問が続くコードを示します。

class countcalls(object):
   "Decorator that keeps track of the number of times a function is called."

   __instances = {}

   def __init__(self, f):
      self.__f = f
      self.__numcalls = 0
      countcalls.__instances[f] = self

   def __call__(self, *args, **kwargs):
      self.__numcalls += 1
      return self.__f(*args, **kwargs)

   def count(self):
      "Return the number of times the function f was called."
      return countcalls.__instances[self.__f].__numcalls

@countcalls
def f():
   print 'f called'

f()
f()
f()
print f.count() # prints 3

私の疑問:

  1. デコレータの前に関数を付ける場合、それはデコレータクラスのオブジェクトをその場で作成していることを意味しますか?私たちの場合、それが言うとき:

    @countcalls def f(): print 'f called'

@countcallsオブジェクトを作成し、countcalls以下の関数をその__init__メソッドに渡すことと同じですか?

  1. __call__3つの議論を取っています。self上記の質問に答える限りは問題ありません。他の2つの議論は一体何ですか:*args, **kwargsそして彼らは何を達成していますか?

  2. どうすればデコレータを上手にできますか?

4

4 に答える 4

5

このコードには奇妙な点があるようです。少し単純なコードについて話しましょう

class countcalls(object):

    def __init__(self, f):
        self._f = f
        self._numcalls = 0

    def __call__(self, *args, **kwargs):
        self._numcalls += 1
        return self._f(*args, **kwargs)

    def count(self):
        return self._numcalls

@countcalls
def f():
    print 'f called'

f()
f()
f()
print f.count() 

# output:
#   f called
#   f called
#   f called
#   3

覚えて

@countcalls
def f():
    print 'f called'

と同じものです

def f():
    print 'f called'
f = countcalls(f)

したがって、デコレータを使用すると、関数は_f属性を使用して格納されます。

インスタンスもそうfです。呼び出すcountcallsときは、このようにして独自のインスタンスの構文を実装します。それで、あなたが電話するとき、何が起こりますか?f(...)f.__call__(...)()f

    def __call__(self, *args, **kwargs):
        self._numcalls += 1
        return self._f(*args, **kwargs)

まず、*argsandを使用します**kwargs。これは、定義ですべての位置引数とキーワード引数をタプルとdictに凝縮し、呼び出しの後半でシーケンスとdictを引数に展開します(詳細については、公式チュートリアルの4.7.4を参照してください)。これが部分的な例です

>>> def f(*args): print args
... 
>>> f(1, 2)
(1, 2)
>>> f()
()
>>> def add(a, b): return a + b
... 
>>> add(*[4, 3])
7
>>> add(**{'b': 5, 'a': 9})
14

したがってdef f(*args, **kwargs): return g(*args, **kwargs)、すべての引数に対してパススルーを実行します。

それとは別に、あなたは__call__このインスタンスに何回参加したか(何回電話をかけたかf)を追跡しているだけです。

それ@dec def f(...): ...はと同じでdef f(...): ... f = dec(f)あり、十分な時間があれば、ほとんどのデコレータをうまく理解できるはずです。すべてのものと同様に、練習はあなたがこれをより速くそしてより簡単にするのを助けるでしょう。

于 2012-06-26T20:40:47.260 に答える
3

デコレータの前に関数を付ける場合、それはデコレータクラスのオブジェクトをその場で作成していることを意味しますか?

見つけるのはとても簡単な方法があります。

>>> @countcalls
... def f(): pass
>>> f
<a.countcalls object at 0x3175310>
>>> isinstance(f, countcalls)
True

あ、はい。

@countcallsは、countcallsオブジェクトを作成し、以下の関数をそのinitメソッドに渡すことと同じですか?

ほとんど。これは、それと同じで、結果を関数の名前に割り当てます

def f():
    print 'f called'
f = countcalls(f)

どうすればデコレータを上手にできますか?

  1. 練習
  2. それらを使用する他の人のコードを読む
  3. 関連するPEPを調査する
于 2012-06-26T20:11:39.193 に答える
2
  1. これは、次と同等です。

    f = countcalls(f)

    言い換えれば、はい、オブジェクトを作成し、そのコンストラクターcountcallsに渡します。f

  2. これらは関数の引数です。あなたの場合、f引数は取りませんが、次のfように呼び出されたとします。

    f(3, 4, keyword=5)

    then *args would contain 3 and 4 and **kwargs would contain the key/value pair keyword=5. For more information about *args and **kwargs, see this question.

  3. Practice makes perfect.

于 2012-06-26T20:11:50.127 に答える
0
  1. はい、そのオブジェクトをdefで指定された名前にバインドすることにも注意してください。

  2. それらについて読んだり、例を読んだり、いくつか遊んだりしてください。

于 2012-06-26T20:11:23.847 に答える