web-dev-qa-db-ja.com

解凍と*演算子

python docsは、このコードをZipの逆演算として提供します:

>>> x2, y2 = Zip(*zipped)

特に、「*演算子と組み合わせたZip()を使用して、リストを解凍できます」。この場合、*演算子がどのように機能するかを誰かに説明できますか?私が理解している限り、*は2項演算子であり、乗算または浅いコピーに使用できます...どちらもここでは当てはまらないようです。

31
Leah Xue

このように使用する場合、*(アスタリスク、円の中では「splat」演算子としても知られています)は、リストから引数をアンパックするシグナルです。例を含むより完全な定義については、 http://docs.python.org/tutorial/controlflow.html#unpacking-argument-lists を参照してください。

22
Philip Southam

hammar's answerZip()関数の場合に逆転がどのように機能するかを説明しますが、より一般的な意味で引数のアンパッキングを見ると役立つ場合があります。いくつかの引数を取る単純な関数があるとしましょう:

_>>> def do_something(arg1, arg2, arg3):
...     print 'arg1: %s' % arg1
...     print 'arg2: %s' % arg2
...     print 'arg3: %s' % arg3
... 
>>> do_something(1, 2, 3)
arg1: 1
arg2: 2
arg3: 3
_

引数を直接指定する代わりに、それらを保持するためのリスト(またはその問題についてはタプル)を作成し、Python tounpack関数の引数としてリストし、その内容を使用します。

_>>> arguments = [42, 'insert value here', 3.14]
>>> do_something(*arguments)
arg1: 42
arg2: insert value here
arg3: 3.14
_

引数が十分にない(または引数が多すぎる)場合、これは正常に動作します。

_>>> arguments = [42, 'insert value here']
>>> do_something(*arguments)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/blair/<ipython console> in <module>()

TypeError: do_something() takes exactly 3 arguments (2 given)
_

関数を定義するときに同じ構成を使用して、任意の数の位置引数を受け入れることができます。それらはタプルとして関数に与えられます:

_>>> def show_args(*args):
...     for index, value in enumerate(args):
...         print 'Argument %d: %s' % (index, value)
...
>>> show_args(1, 2, 3)
Argument 0: 1
Argument 1: 2
Argument 2: 3
_

そしてもちろん、2つの手法を組み合わせることができます。

_>>> show_args(*arguments)
Argument 0: 42
Argument 1: insert value here
_

2つのアスタリスク(_**_)と辞書を使用して、キーワード引数で同様のことを行うことができます。

_>>> def show_kwargs(**kwargs):
...     for arg, value in kwargs.items():
...         print '%s = %s' % (arg, value)
...
>>> show_kwargs(age=24, name='Blair')
age = 24
name = Blair
_

そしてもちろん、辞書を介してキーワード引数を渡すことができます:

_>>> values = {'name': 'John', 'age': 17}
>>> show_kwargs(**values)
age = 17
name = John
_

2つを混在させることは完全に許容され、関数には常に必須の引数とオプションの追加の引数を指定できます。

_>>> def mixed(required_arg, *args, **kwargs):
...     print 'Required: %s' % required_arg
...     if args:
...         print 'Extra positional arguments: %s' % str(args)
...     if kwargs:
...         print 'Extra keyword arguments: %s' % kwargs
...
>>> mixed(1)
Required: 1
>>> mixed(1, 2, 3)
Required: 1
Extra positional arguments: (2, 3)
>>> mixed(1, 2, 3, test=True)
Required: 1
Extra positional arguments: (2, 3)
Extra keyword arguments: {'test': True}
>>> args = (2, 3, 4)
>>> kwargs = {'test': True, 'func': min}
>>> mixed(*args, **kwargs)
Required: 2
Extra positional arguments: (3, 4)
Extra keyword arguments: {'test': True, 'func': <built-in function min>}
_

オプションのキーワード引数を取り、デフォルト値が必要な場合は、ディクショナリを扱っていることを忘れないでください。したがって、キーが存在しない場合に使用するデフォルト値でget()メソッドを使用できます。

_>>> def take_keywords(**kwargs):
...     print 'Test mode: %s' % kwargs.get('test', False)
...     print 'Combining function: %s' % kwargs.get('func', all)
... 
>>> take_keywords()
Test mode: False
Combining function: <built-in function all>
>>> take_keywords(func=any)
Test mode: False
Combining function: <built-in function any>
_
61
Blair

Zip(*zipped)は、「zippedの各要素をZipへの引数としてフィードする」ことを意味します。 Zipは、行列を転置することと似ていますが、行列を転置すると、開始した場所に戻ります。

>>> a = [(1, 2, 3), (4, 5, 6)]
>>> b = Zip(*a)
>>> b
[(1, 4), (2, 5), (3, 6)]
>>> Zip(*b)
[(1, 2, 3), (4, 5, 6)]
21
hammar

Zip()が何をするかを本当に理解すれば、それは実際には非常に簡単です。

Zip関数は、いくつかの引数(すべて反復可能な型)を取り、それぞれの位置に従ってこれらの反復可能なオブジェクトからアイテムをペアにします。

たとえば、2つの引数ranked_athletes, rewardsZipに渡されたとすると、関数呼び出しZip(ranked_athletes, rewards)は次のようになります。

  • 最初にランクされた選手(ポジションi = 0)と最初/最高の報酬(ポジションi = 0)をペアにした選手
  • 次の要素を移動します、i = 1
  • 2番目のアスリートを報酬とペアにします。2番目はrewardからです。
  • ...

これは、アスリートまたは報酬がなくなるまで繰り返されます。たとえば、2016年のオリンピックで100メートルを取った場合、Zipの報酬は次のようになります。

ranked_athletes = ["Usain Bolt", "Justin Gatlin", "Andre De Grasse", "Yohan Blake"]
rewards = ["Gold medal", "Silver medal", "Bronze medal"]
Zip(ranked_athletes, rewards)

次のタプル(ペア)の反復子を返します。

('Usain Bolt', 'Gold medal')
('Justin Gatlin', 'Silver medal')
('Andre De Grasse', 'Bronze medal')

Yohan Blakeに報酬がないことに注目してください。

*演算子です。これはさらに簡単です。リスト[1, 2]がある場合、これにより1, 2に展開されます。基本的には、1つのオブジェクトを(リストのサイズと同じくらい)多くのオブジェクトに変換します。

したがって、これら2つを組み合わせる場合、Zip(*x)は実際には次のことを意味します:このオブジェクトのリストを取得し、多くのオブジェクトにアンパックして、これらすべてのオブジェクトからアイテムをペアにします彼らのインデックスによると。オブジェクトが反復可能である場合(たとえば、リストなど)にのみ意味があります。そうでない場合、indexの概念は実際には意味がありません。

段階的に実行すると、次のようになります。

>>> print(x)              # x is a list of lists 
[[1, 2, 3], ['a', 'b', 'c', 'd']]

>>> print(*x)             # unpack x
[1, 2, 3]  ['a', 'b', 'c', 'd']

>>> print(list(Zip(*x)))  # And pair items from the resulting lists
[(1, 'a'), (2, 'b'), (3, 'c')]

この場合、print(list(Zip(x)))を呼び出すと、x(2つのリスト)のアイテムを何もなしにペアにすることに注意してください(他にイテレート可能な他のペアはないため)。

[  ([1, 2, 3],    ),  (['a', 'b', 'c', 'd'],    )]
               ^                              ^
    [1, 2, 3] is paired with nothing          |
                                              |
                        same for the 2nd item from x: ['a', 'b', 'c', 'd']

Zipがどのように機能するかを理解するもう1つの良い方法は、独自のバージョンを実装することです。これは、多かれ少なかれZipと同じ仕事をしますが、2つのリストの場合に限定されます(代わりに多くのイテラブル):

def Zip_two_lists(A, B):
    shortest_list_size = min(len(A), len(B))
    # We create empty pairs
    pairs = [Tuple() for _ in range(shortest_list_size)]
    # And fill them with items from each iterable 
    # according to their the items index:
    for index in range(shortest_list_size):
        pairs[index] = (A[index], B[index])
    return pairs

print(Zip_two_lists(*x))
# Outputs: [(1, 'a'), (2, 'b'), (3, 'c')]

print(list(Zip_two_lists(*x)))を呼び出さなかったことに注意してください。これは、この関数が実際のZipとは異なり、ジェネレーター(イテレーターを構成する関数)ではなく、メモリ内にリストを作成するためです。したがって、この関数はそれほど良くありません。より良い Pythonのドキュメントで実際のZipへの近似 を見つけることができます。多くの場合、このドキュメントの周囲にある同等のコードを読むことをお勧めします。これは、曖昧さなく関数の機能を理解するための良い方法です。

1
cglacet