0
0

More than 1 year has passed since last update.

Pythonの関数デコレータに入門する

Last updated at Posted at 2022-10-30

概要

Pythonを使っているとdefの上部に@がくっついていることがありますよね。


@main.result_callback()
def run(a, b):
    ....  

これ、関数デコレータ(以下、デコレータ)と呼ばれるものらしいのですが、調べてみてもよくわからないし、使い道もサッパリだったので無視してました。

だがしかし。

大量にデコレータがついているソースコードに遭遇してしまい、どうしても理解しないといけない状況になりました。

仕方なく重い腰をあげて勉強してみます。

デコレータの基本の「き」

デコレータの最低限の動きを確認するために次のコードを実行してみます。

def decorator(f):
  def inner():
    print('before')
    retval = f()
    print('after')
    return retval
  return inner
  
@decorator
def example():
  print('inside')

example()

実行結果

before
inside
after

デコレータを少しでも調べたことがある人は、デコレータdecoratorを付与した状態でexample()を実行することはdecorator(example)を実行することと同じであるという説明をみたことがあると思います。そのため、decoratorの引数fをexample関数として読み替えれば最低限の動きは理解できるかと思います。

print('before')
retval = example()
print('after')

ちなみにexample関数はretval = example()と代入しているだけですが、これで関数が実行されます。

例えば下記のように書いてみるとhoge関数は実行されてhelloが表示されます。

def hoge():
  print('hello')

f = hoge
x = f()

実行結果

hello

デコレータを使うことで、ある関数(example関数)の処理を変更せずにその前後に別の処理を入れることができることがわかりました。

これだけじゃ使い物にならない

実行関数に引数を伴う場合

example()関数に引数を伴う場合はエラーになってしまいます。

def decorator(f):
  def inner():
    print('before')
    retval = f()
    print('after')
    return retval
  return inner
  
@decorator
def example(x):
  print(x)
  print('inside')

example('hello')
TypeError: inner() takes 0 positional arguments but 1 was given

この場合は次のようにdecorator(), inner()に可変長引数*args, **kwargsを指定すればOKです。

def decorator(f, *args, **kwargs):
  def inner(*args, **kwargs):
    print('before')
    retval = f(*args, **kwargs)
    print('after')
    return retval
  return inner
  
@decorator
def example(x):
  print(x)
  print('inside')

example('hello')

実行結果

before
hello
inside
after

デコレータに引数を指定したい場合

デコレータdecorator()に引数を指定したい場合は次のように書くことで実現できます。

def decorator(start, end):
  def _time_args(f):
    def inner(*args, **kwargs):
        print(start)
        print('before')
        retval = f(*args, **kwargs)
        print('after')
        print(end)
        return retval
    return inner
  return _time_args 

@decorator('開始', '終了')
def example(x):
  print(x)
  print('inside')

example('hello')

実行結果

開始
before
hello
inside
after
終了

うーん、どんどん入れ子になって混乱してきた。

さいごに

デコレータの理解を深めるにはPythonの理解をもっと深めていく必要があるなあーと感じました。

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0