python3x: parameterとして出てくる*argsと**kwargsって何を意味するの?

  • 18
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

元々はというか本来は***として成り立っている。が界隈では通例として*args**kwargsとして表記されているだけらしい。主な違いは変数の補完の仕方がtupledictionaryかというだけの違い。

何に役に立つのか

*args

def foo(*args):
    for a in args:
        print a

foo(1) #1
foo(1,2,3) #1\n2\n3\n

仮の引数として*argsといれおけば、関数作成中に「引数をいくつ取るかまだ未定」なんていう時に
argsがtupleのプレースホルダーとして入ってきた引数全てを引き取る役割を果たしてくれる。

def test_var_args_call(arg1, arg2, arg3):
    print ("arg1:", arg1)
    print ("arg2:", arg2)
    print ("arg3:", arg3)

args = ("two", 3)
test_var_args_call(1, *args)

#result
arg1: 1
arg2: two
arg3: 3

先にargs変数を用意しておいて後から*argsでかぶせるイメージ。最初のparameter:1 はarg1として受け取り、残りは全てarg2arg3の中に配置される。ちなみにこうやって最初に引数の数を指定して後から関数で入れていく場合は最初に定義した関数が受け取れる引数の数の分だけのみしか受け取れないので注意。def function(arg1, *args)であれば最初の一つ目だけarg1に行き残りは全て*argsが引き取る。

def test_var_args(farg, *args):
    print "formal arg:", farg
    for arg in args:
        print "another arg:", arg

test_var_args(1, "two", 3)

#result
formal arg: 1
another arg: two
another arg: 3

こういう使い方をしたい場合は引数が何個来るかわからないことが多いのでfor loopなどで「出てきた数分だけxの処理を行う」としてしまったほうが楽。細かいことではあるがここでprintの代わりにreturnを使うと最初の一つだけしか表示されない。その理由は恐らくreturnには一度その処理をしてしまうとループを終わらせてしまうという特徴があるからだと思う。思いつく対策としてはループの中でリストにためておいてループの外でreturn statementを実行する。

def test_var_args(farg, *args):
    print("formal arg:", farg)
    for arg in args:
        print("another arg:", arg)

    return arg

#result
formal arg: 1
another arg: two
another arg: 3
3

こんな感じで無理矢理やってみたが最後の3しか出せなかった。理由はargの最後が3にくっついたままループが終わっているからだ。

The for-loop runs each statement in it for however so many times.. if one of your statements is a return, then the function will return when it hits it. This makes sense in, for example, the following case:

def get_index(needle, haystack):
    for x in range(len(haystack)):
        if haystack[x] == needle:
            return x

Here, the function iterates until it finds where the needle is in the haystack, and then returns that index (there's a builtin function to do this, anyways). If you want the function to run for however many times you tell it to, you have to put the return AFTER the for-loop, not inside it, that way, the function will return after the control gets off the loop

def add(numbers):
    ret = 0
    for x in numbers:
        ret = ret + x
    return ret # needs to be outside the loop

**kwargs

一言でまとめると、*がtupleで変数を管理するのに対し**はdictionaryとして保存する。つまりkeyvalueの2つを引数として取ることができる。

def bar(**kwargs):
    for a in kwargs:
        print(a, kwargs[a])

bar(name="your mom", age=12, hotness=1) #hotness 1\nage 12\nname your mom

*l

*args**kwargsに加えて*lというイディオムもある。主な使われ方は変数として受け取ったリストを開いて中身を取り出してtupleの中に保存してくれる。やり方は変数lの値にリストを加え(もしくは定義しておいて後で送るetc)、そのままfunc(*l)で呼んであげるだけだ。ただ呼ぶ方の関数にも同じ数のparameterを用意させる必要がある。

def foo(a, b):
    print(a, b)

l = [1, 2]
foo(*l) #1, 2

追記

ちなみに*args**kwargsも関数を受け取るターゲットとしか受け取ることができない。どういうことかというと

def make_averaged(*args):
    return *args / len(str(*args))
# causes syntax error

syntax errorを起こしてしまうので*の後につけた名前だけで変数を取り扱うように。たまに忘れてしまうので備忘録として。

参考にしたリンク

- return statement in for loop