質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.81%

decoratorのサンプルコードでのargsとkwargsの役割について

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 965

このようなサンプルコードをよく見かけますが、出力が同じでargsとkwargsのある意味がわかりません。 なんとなく、雰囲気でみなさん書いているのか、もしくは僕の理解が間違っているのか気になるのでご確認させてください。 個人的には、argsとkwargsはなくていいと思っています。さらに、コード内で入れ子にして def wrapper(): を記載する必要があるのかも疑問です、意図が理解できる方はおしえていただけませんか?何の確認のために記載するかわかりませんが、仮になにか確認のためにdef wrapper(): を書いているとしたら、どんな時にこういうコードを実際の開発でかきますか?

argsとkwargsの理解は以下です。

*args: 複数の引数をタプルとして受け取る

def my_sum2(*args):
    print('args: ', args)
    print('type: ', type(args))
    print('sum : ', sum(args))

my_sum2(1, 2, 3, 4)
# args:  (1, 2, 3, 4)
# type:  <class 'tuple'>
# sum :  10

**kwargs: 複数のキーワード引数を辞書として受け取る
**をつけた引数を定義すると、任意の数のキーワード引数を指定することができる。関数の中では引数名がキーkey、値がvalueとなる辞書として受け取られる。

def func_kwargs(**kwargs):
    print('kwargs: ', kwargs)
    print('type: ', type(kwargs))

func_kwargs(key1=1, key2=2, key3=3)
# kwargs:  {'key1': 1, 'key2': 2, 'key3': 3}
# type:  <class 'dict'>

decoratorのサンプルコードでのargsとkwargs

argsとkwargs記載有りのコード

def deco(func):
    def wrapper(*args, **kwargs):
        print('--start--')
        func(*args, **kwargs)
        print('--end--')
    return wrapper

@deco
def test():
    print('Hello Decorator')

test()

## -- End pasted text --
--start--
Hello Decorator
--end--

argsとkwargs記載なしのコード

def deco(func):
    def wrapper():
        print('--start--')
        func()
        print('--end--')
    return wrapper

@deco
def test():
    print('Hello Decorator')

test()

## -- End pasted text --
--start--
Hello Decorator
--end--

この def wrapper()をデコレーターにすると(@wraps(func))どういうメリットがあるのでしょうか?
@wraps(func)

発展型の理解

from functools import wraps

def validation_objecs(func):
    @wraps(func)
    def wrapper(request):
        ....
        ....
        ....
        return func(request)
    return wrapper

....
....
....


@validation_objecs
def start_batch(request):
      ....
        ....
        ....
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

decorateする関数に引数がない場合は*args**kwargsはなくても問題ありませんが、引数を要求する関数をdecoratorでwrapするときには便利です。

def deco(func):
    def wrapper(*args, **kwargs):
        print('--start--')
        func(*args, **kwargs)
        print('--end--')
    return wrapper

@deco
def test(name):
    print(f'Hello Decorator - {name}')

test("master")
# --start--
# Hello Decorator - master
# --end--

@wrapsなしだと何をwrapしたのか分からなくなってしまう問題があります。

def deco(func):
    def wrapper(*args, **kwargs):
        print('--start--')
        func(*args, **kwargs)
        print('--end--')
    return wrapper

@deco
def test(name):
    """How to use"""
    print(f'Hello Decorator - {name}')

print(test.__doc__)
# None
print(test)
# <function deco.<locals>.wrapper at 0x102dc38c8>

@wrapsを使うと、もとのオブジェクトの属性を引き継ぐことができます。

def deco(func):
    @wraps
    def wrapper(*args, **kwargs):
        print('--start--')
        func(*args, **kwargs)
        print('--end--')
    return wrapper

@deco
def test(name):
    """How to use"""
    print(f'Hello Decorator - {name}')

print(test.__doc__)
# How to use
print(test)
# <function test at 0x102e98488>

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/08/10 12:23 編集

    ありがとうございます。
    > 単純にdecorateしてしまうと、もともとの関数に仕込んでいたdocstringなどの属性にアクセスできなくなってしまう問題があった気がします。

    この点については、おっしゃる通りです。docstringなどの属性にアクセスできないです。しかし、@wrapsをかくことでどう処理の順番になるのか、困惑しています。
    return wrapperがどこに正確には変えるのかが気になっています。

    キャンセル

  • 2018/08/10 13:50

    仕組みを全部理解しているわけではないので、詳細はとりあえずソースコードを追いかけてみてくだされ。もしくは他の回答を待つのでもいいと思います。
    https://github.com/python/cpython/blob/3.7/Lib/functools.py

    キャンセル

  • 2018/08/10 13:58

    そうですよね、不毛な質問でした。ありがとうございます!

    キャンセル

0

下記、コードは、start_batchが走る前に、start_batchの引数であるrequestオブジェクトを引数としてデコレーター(def validation_objecs(func):)におくり、def wrapper(request): を入れ子にして書くことでその引数を関数内にひきつぐようにしている。return func(request)で def wrapperの処理結果を出力し、その出力結果をreturn wrapperでまとめてデコレートされたstart_batchに値を渡す。

return func(request)は、デコレートされたメソッドを実行している。func = start_batch。

*@wraps(func)はデコレートされたメソッド内のdocsを引き継いてくれる。@wrapsがないと引き継がれない。
*def wrapper(request):のwrapperとう命名は実際は何でもよいのだが、最後にreturnする名前と合わせる必要がある。

 1 def deco(func):
  2     @wraps(func)
  3     def w(*args, **kwargs):
  4         print('--start--')
  5         func(*args, **kwargs)
  6         print('--end--')
  7         print("w"+str(w))
  8
  9     return w
 10
 11 @deco
 12 def test(name):
 13     print(f'Hello Decorator - {name}')
 14
 15 test("master")
~
from functools import wraps

def validation_objecs(func):
    @wraps(func)
    def wrapper(request):
        ....
        ....
        ....
        return func(request)
    return wrapper

....
....
....


@validation_objecs
def start_batch(request):
      ....
        ....
        ....

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.81%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る