args','**','**kwargs' を理解する。
オリジナルテキスト '*'、'*args'、'**'、'**kwargs ' を理解する。
私がPythonを学び始めた頃、args,kwargs,*,**の使い方に戸惑ったことがあります。きっと、このことで迷っている人はたくさんいると思います。私はこの投稿でその混乱を解消しようと思います(そしてできれば混乱が少なくなるように)。
以下の5つのステップで理解していきましょう。
- 関数呼び出しで「*」を理解する
- * ' を通して関数の定義を理解する。 関数の定義による「args
- 関数呼び出しによる'**'の理解
- * 関数定義で '* を解く 関数の定義による「kwargs」。
- args', 'kwargs' シナリオを説明するためのアプリケーション例とその理由
関数呼び出しで「*」を理解する
3つの位置引数 "fun" を持つ関数を定義します。
>>> def fun(a,b,c):
... print a,b,c
...
3つの位置引数でこの関数を呼び出す
>>> fun(1,2,3)
1 2 3 #output
この関数を3つの位置の引数をinとoutで呼び出すと、3つの引数が出力されることがわかると思います
今度は、3つの整数で系列を定義し、'*'を使用します。
>>> l = [1,2,3]
>>> fun(*l)
1 2 3 #output
は何をしたのですか?
配列 'l' の値を位置引数として分割し、それらの位置引数を関数 'fun' に渡して呼び出す。
つまり、系列を分割して位置引数を渡すということは、l = [1,2,3] なので fun(*l) は fun(1,2,3) と等価であるということです。
他の値をシリーズで使ってみる
>>> l = [4,8,0]
>>> fun(*l)
4 8 0 #output
次に、配列に4つの値を入れてみて、関数を呼び出すとどうなるでしょうか。
>>> l = [3,6,9,1]
>>> fun(*l)
Traceback (most recent call last):
File "
"
, line 1, in <module>
TypeError: fun() takes exactly 3 arguments (4 given)
この呼び出しでは正しい結果が得られず、TypeWrror例外が発生する。エラー "fun() takes exactly 3 arguments (4 given) " を見るのは簡単です。
なぜこのようなことが起こるのでしょうか?
配列 'l' には4つの値が含まれています。そこで、'fun(*l)'を呼び出そうとすると、'l'の値が分割されて位置引数として関数funに渡されてしまいます。しかし、'l' には4つの値があり、'fun(*l)' を呼ぶことは 'fun(3,6,9,1)' を呼ぶことと同じであり、関数 'fun ' は3つの位置引数だけで定義されているので、このエラーが発生するのである。同様に、系列'l'の2つの値の場合にも同じ手順で、エラー内容に注意してください。
>>> l = [7,4]
>>> fun(*l)
Traceback (most recent call last):
File "
"
, line 1, in <module>
TypeError: fun() takes exactly 3 arguments (2 given)
位置引数に '*l' を混ぜる
>>> fun(23, *l)
23 7 4
ここでは、位置の引数23と、系列「l」から削除された2つの値7と4が与えられているので、3つの引数23、7、4が関数「fun」に渡されます。
関数の定義を通して'*args'の意味を理解する
関数の定義を変更する。
>>> def fun(*args):
... print args
...
この関数を呼び出すための位置引数を渡します
>>> fun(13)
(13,)
複数の引数を渡してこの関数を呼び出す
>>> fun(11,93,43)
(11, 93, 43)
関数定義で'*args'は何をするのですか?
通常の引数のリストではなく、タプルを位置引数として受け取ります。この場合、"args"はタプルになります。この"common arguments"の部分の説明は、次の例で明らかになるので、理解するのに心配する必要はありません。前の例では、関数が呼び出されて "args" を表示するとき、タプルに含まれるすべての値を表示します。
ここで、"*args" と "regular argument list" を混ぜるように関数を再定義してみます。
>>> def fun(a, *args):
... print "a is ", a
... print "args is ", args
...
この関数定義では、引数 "a" は "regular argument list" を表しています。
この関数を呼び出すには、4つの位置引数を渡します。
>>> fun(11,12,34,43)
a is 11
args is (12, 34, 43)
a'が最初の位置引数である11とプリントアウトされることは容易に理解できる。a' の後には、'*args' という一つの引数があるだけです。したがって、'args'は通常の引数以外の位置引数をタプルとして受け取ります。したがって、タプルargsは12,34,43をタプルとして受け取る。
また、この関数を呼び出す際に位置引数を渡すことができます。
>>> fun(91)
a is 91
args is ()
ここでは、渡した唯一の引数が通常の引数'a'に代入されています。したがって、'args'は空のタプルを受け取る。
これで"args"が得られたので、やりたいことをやるために必要な値を抽出することができる。fun"を再定義します。
>>> def fun(a, *args):
... print a
... print "args can receive a tuple of any number of arguments, let's print all that."
... for arg in args:
... print arg
...
ここで、この関数を呼び出すために、任意の引数を渡します。
>>> fun(1,5,6,7)
1
args can receive a tuple of any number of arguments, let's print all that.
5
6
7
args' はタプルなので、それに対して反復処理を行うことができます。
では、得られるすべての引数を使うシナリオを考えてみよう。2つの関数を使い、1つ目の関数には任意の数の引数を与え、もう1つの関数で1つ目以外の引数の合計を計算する必要があります。奇妙な使用例ですが、これまでやってきたことを復習してみましょう。我々の目標は、一方の関数で変数の引数を取得し、その引数をもう一方の関数に食わせることである。
最初のステップでは、合計を計算する関数を書きます。このユースケースでは、この関数は最初の関数の中で適用されます。
>>> def calculate_sum(*args):
... return sum(args)
...
この関数では、タプルまたは配列を引数として受け取り、そのタプルの全要素の合計を返す組み込み関数 'sum' を使っています。関数の定義からわかるように、'args'は関数の位置に渡された引数を含むタプルを受け取ります。したがって、'args' は関数 'sum' の引数として簡単に使用されるタプルである。次に、任意の数の引数を持つ別の関数を定義し、前の関数を使用して、最初の引数を除いたすべての引数の合計を計算する。
>>> def ignore_first_calculate_sum(a,*iargs):
... required_sum = calculate_sum(*iargs)
... print "sum is ", required_sum
...
この関数には、任意の数の引数を渡すことができます。最初の引数は通常の引数 'a' で受け取り、他の引数はタプルとして 'iargs' で受け取ります。今回考えているように、最初の引数を除いたすべての引数の合計が計算される。したがって、最初の引数を受け取るために'a'を使用し、他の引数を含むタプルとして'iargs'を使用しています。関数 'calculate_sum' を使うが、 'calculate_sum' は、タプルとして 'args' に渡される複数の位置の引数を必要とする . そこで、関数 'ignore_first_calculate_sum' はタプル 'iargs' を分割し、その要素を位置引数として 'calculate_sum' に渡す必要があります。 sum' は、タプル 'iargs' を分割し、その要素を位置引数として渡します。タプルは'*'で分割されていることに注意してください。
そこで、『required_sum=calculate_sum(*iargs)』をこのように呼び出します。
required_sum=calculate_sum(iargs)' はこのように呼び出すことはできません。なぜなら、'calculate_sum' に渡す前に値をアンパックする必要があるからです。なぜなら、'calculate_sum'に渡す前に値を解凍する必要があるからです。'*'がなければ、値を解凍することができず、目的の動作をさせることはできません。この関数は次のように呼び出される。
>>> ignore_first_calculate_sum(12, 1,4,5)
sum is 10
>>> ignore_first_calculate_sum()
Traceback (most recent call last):
File "
"
, line 1, in <module>
TypeError: ignore_first_calculate_sum() takes at least 1 argument (0 given)
目的の結果を得る。
関数呼び出しによる'**'の理解
3つの引数を持つ関数を定義し、それを複数の方法で呼び出す。
>>> def fun(a, b, c):
... print a, b, c
...
>>> fun(1,5,7)
1 5 7
>>> fun(a=1,b=5,c=7)
1 5 7
を使用します。 関数を呼び出すには、辞書が必要です。注意:** 関数呼び出しで "*" を使用すると、タプルが必要になります。 "の場合、辞書が必要です**。
>>> d={'b':5, 'c':7}
>>> fun(1, **d)
1 5 7
関数呼び出しで "**"は何をするのですか?
辞書を解凍し、辞書内のデータ項目をKey-Valueの引数として関数に渡します。つまり、"fun(1, **d)" は "fun(1, b=5, c=7)" と同じように記述されます。
理解を深めるために、もう少し例を挙げます。
>>> d = {'c':3}
>>> fun(1, 4, **d)
1 4 3
>>> d = {'a':7, 'b':3, 'c':8}
>>> fun(**d)
7 3 8
いくつかエラーを出してみましょう。
>>> d = {'a':7, 'b':3, 'c':8, 'd':90}
>>> fun(**d)
Traceback (most recent call last):
File "
"
, line 1, in <module>
TypeError: fun() got an unexpected keyword argument 'd'
この呼び出しは 'fun(a=7, b=3, c=8, d=90)' と同等ですが、この関数は3つの引数しか取らないので、TypeError が発生します。
>>> d = {'a':7, 'b':3, 'd':90}
>>> fun(**d)
Traceback (most recent call last):
File "
"
, line 1, in <module>
TypeError: fun() got an unexpected keyword argument 'd'
fun(**d) は fun(a=7, b=3, d=90) と同じ意味です。関数 "fun" に好きな数の引数を渡しますが、引数リストに 'd' がなく、呼び出し中の関数に 'd' キーワード引数を渡すと TypeError が発生します。
つまり、"**" は辞書を展開し、すなわち辞書内のキーと値のペアをキーワード引数として、これらは呼び出される関数にキーワード引数として送られます。 "*" はリスト/タプルを展開し、すなわちリスト内の値を位置引数として、これらは呼び出される関数に位置引数として送られます。
関数定義から「**kwargs」の意味を理解する
関数 "fun" を再定義します。
>>> def fun(a, **kwargs):
... print a, kwargs
...
この関数は位置引数を1つだけ使用します。なぜなら、通常の引数リストには変数 'a' が1つだけ含まれているからです。しかし、"**kwargs"を使用すると、複数のキー-バリュー引数を渡すことができます。
>>> fun(1, b=4, c=5)
1 {'c': 5, 'b': 4}
>>> fun(45, b=6, c=7, d=8)
45 {'c': 7, 'b': 6, 'd': 8}
関数定義における "**kwargs" とはどのような意味ですか?
関数を "**kwargs" で定義すると、kwargs は通常の引数リスト位置に加えて、キーバリュー引数の辞書を受け取ります。この場合、'kwargs' は辞書である。
関数を再定義します。
>>> def fun(a, **kwargs):
... print "a is ", a
... print "We expect kwargs 'b' and 'c' in this function"
... print "b is ", kwargs['b']
... print "c is ", kwargs['c']
...
>>> fun(1, b=3,c=5)
a is 1
We expect kwargs 'b' and 'c' in this function
b is 3
c is 5
エラーコールです。
>>> fun(1, b=3, d=5)
a is 1
We expect kwargs 'b' and 'c' in this function
b is 3
c is
Traceback (most recent call last):
File "
"
, line 1, in <module>
File "
"
, line 5, in fun
KeyError: 'c'
上記の呼び出しでは、位置パラメーター 'a' とキーパラメーター 'b' の両方が出力される。渡されたもう一つのキーパラメータは 'd' で、この関数はキーパラメータ 'c' を受け取り、辞書 'kwargs' からそれを取得します。しかし、キー値 'c' は渡されないので、KeyError が発生する。キー値'c'が渡された場合は、このエラーは発生しません。
>>> fun(1, b=3, d=5, c=9)
a is 1
We expect kwargs 'b' and 'c' in this function
b is 3
c is 9
関数の引数リストに'**kwargs'があるので、任意のKey-Value引数を渡すことができる。上記の呼び出しは "d" を渡しますが、この関数はそれを使用しません。
またエラーです。
>>> fun(1, {'b':2, 'c':34})
Traceback (most recent call last):
File "
"
, line 1, in <module>
TypeError: fun() takes exactly 1 argument (2 given)
エラーが示すように、関数 'fun' は位置引数を1つしか取らないのに、2つの引数を与えています。kwargs' は key-value の引数を辞書として受け取りますが、'kwargs' に位置引数として辞書を渡すことはできません。このように呼び出すことができる。
>>> fun(1, **{'b':2, 'c':34})
a is 1
We expect kwargs 'b' and 'c' in this function
b is 2
c is 34
辞書の前に "**" をつけて、辞書を展開し、辞書内のデータ項目を key-value 引数として渡します。
args', 'kwargs' シナリオを説明するためのアプリケーション例とその理由
クラスを継承してメソッドをオーバーライドするときは、必ず「*args」と「**kwargs」を使って、受け取った位置引数とキー引数を親メソッドに与える必要があります。例によって、よりよく理解することができます。
>>> class Model(object):
... def __init__(self, name):
... self.name = name
... def save(self, force_update=False, force_insert=False):
... if force_update and force_insert:
... raise ValueError("Cannot perform both operations")
... ... if force_update:
... print "Updated an existing record"
... if force_insert:
... print "Created a new record"
...
クラスを定義し、そのクラスのオブジェクトを作成することができ、そのクラスのオブジェクトは「save()」というメソッドを持ちます。クラスのオブジェクトは、save()メソッドでデータベースに保存できるとします。save()パラメータは、データベースにレコードを作成するか、既存のレコードを更新するかを決定する。
モデル'の動作を持つ新しいクラスを構築します。ただし、このクラスのオブジェクトは、いくつかの条件をチェックした後にのみ保存します。この新しいクラスは 'Model' を継承し、'Model' の 'save()' をオーバーライドします。
>>> class ChildModel(Model):
... def save(self, *args, **kwargs):
... if self.name=='abcd':
... super(ChildModel, self).save(*args, **kwargs)
... else:
... return None
...
対応する保存動作は、実際には「モデル」の「save」メソッドで行われます。そこで、'Model' メソッドの代わりに子クラスの 'save()' メソッドを呼び出します。子である ChildModel の 'save()' は、親である save() が必要とするあらゆる引数を受け取り、親メソッドに渡します。したがって、サブクラスの 'save()' メソッドは、引数リストに "*args" と "**kwargs" を持ち、通常の引数リスト以外の任意の位置やキーバリューの引数を取ることができます。
以下はChildModelエンティティを作成し、保存しています。
>>> c=ChildModel('abcd')
>>> c.save(force_insert=True)
Created a new record
>>> c.save(force_update=True)
Updated an existing record
ここでは、パートタイムの引数がオブジェクトのsave()メソッドに渡されています。これは、"kwargs" でキーワード引数を含む辞書を受け取っています。そして、"**"を使ってこの辞書をキーワード引数として展開し、スーパークラスのsave()に渡しているのです。つまり、スーパークラスsave()はキーワード引数「force_insert」を取得し、それに従って動作していたのです。
キーワードだけの引数と位置だけのパラメータ
関連するPEP
def name(positional_only_parameters, /, positional_or_keyword_parameters,
*, keyword_only_parameters):
関連
-
[解決済み】TypeError: 系列を <class 'float'> に変換することができません。
-
[解決済み] '_pywrap_tensorflow_internal' という名前のモジュールはありません。
-
表示名がない、$DISPLAY環境変数がない、というエラーを報告する
-
[解決済み] Pycharmの終了コード0
-
[解決済み] 「ゼロで割っていない時に「ログでゼロで割るが発生しました。
-
[解決済み] x = os.system(...)の戻り値 [重複]。
-
[解決済み] Tkinterのコマンド "iconbitmap "を使ってウィンドウのアイコンを設定する
-
LinearAlgebraError: SVDが収束しなかった(PYTHON)
-
python オブジェクトはアイテムの割り当てをサポートしない
-
AttributeError module pandas has no attribute dataframe
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Pythonにおけるεの値
-
[解決済み] ValueErrorです。ブーリアン値のみのDataFrameを渡さなければならない
-
[解決済み] KeyError: 'plotly_domain' です。
-
[解決済み] Python AttributeError: 'module' オブジェクトに 'Serial' 属性がない [重複] 。
-
[解決済み] NumPyの配列を切り詰めずに、完全に表示するにはどうしたらよいですか?
-
[解決済み] Djangoです。フォームのメールフィールドは、アドレスがあっても 'this field cannot be null/this field cannot be blank' を返します。
-
[解決済み] Python 3でexecfileの代替?[重複]です。
-
[解決済み] ハウツー Pyspark データフレームの永続的な使用とリードバック
-
[解決済み] tensorflowの読み込みエラー - "cudart64_80.dll "が見つかりませんでした。
-
ImportError: tensorflow.tensorboard.tensorboard' という名前のモジュールはありません。