Распаковка и оператор *
Документы python предоставляют этот код как обратную операцию zip:
>>> x2, y2 = zip(*zipped)
В частности, "zip() в сочетании с оператором * можно использовать для распаковки списка". Может ли кто-нибудь объяснить мне, как работает оператор * в этом случае? Насколько я понимаю, * является двоичным оператором и может использоваться для умножения или неглубокой копии... ни то, что кажется здесь.
Ответы
Ответ 1
При использовании этого типа * (звездочка, также известная в некоторых кругах как оператор "splat" ), является сигналом для распаковки аргументов из списка. См. http://docs.python.org/tutorial/controlflow.html#unpacking-argument-lists для более полного определения с примерами.
Ответ 2
Хотя hammar answer объясняет, как работает реверсирование в случае функции zip()
, может быть полезно посмотреть на распаковку аргументов в более общем смысле. Скажем, мы имеем простую функцию, которая принимает некоторые аргументы:
>>> 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 о распаковке этого списка и использовать его содержимое в качестве аргументов функции:
>>> 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
И, конечно, вы можете объединить два метода:
>>> show_args(*arguments)
Argument 0: 42
Argument 1: insert value here
Вы можете сделать аналогичную вещь с аргументами ключевого слова, используя двойную звездочку (**
) и словарь:
>>> 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
Абсолютно приемлемо смешивать два, и вы всегда можете иметь необходимые аргументы и необязательные дополнительные аргументы функции:
>>> 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>
Ответ 3
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)]
Ответ 4
Это на самом деле довольно просто, когда вы действительно понимаете, что делает zip()
.
Функция zip
принимает несколько аргументов (все итерируемого типа) и объединяет элементы из этих итераций в соответствии с их соответствующими позициями.
Например, скажем, у нас есть два аргумента ranked_athletes, rewards
передаваемые zip
, вызов функции zip(ranked_athletes, rewards
) будет:
- пара спортсмен, который занял первое место (позиция я = 0) с первой/лучшей наградой (позиция я = 0)
- это переместит следующий элемент, я = 1
- соедините 2-го спортсмена с его наградой, 2-го с
reward
. - ...
Это будет повторяться до тех пор, пока не останется ни спортсмена, ни награды. Например, если мы возьмем 100 метров на Олимпийских играх в 2016 году и 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')
Обратите внимание, что Йохан Блейк не имеет награды.
Теперь оператор *
, это еще проще, если у вас есть список [1, 2]
это распаковывает его в 1, 2
. Он в основном превращает один объект во множество (столько же, сколько размер списка).
Таким образом, если мы объединяем эти два, zip(*x)
фактически означает: взять этот список объектов, распаковать его со многими объектами и связать элементы из всех этих объектов в соответствии с их индексами. Это имеет смысл, только если объекты являются итеративными (например, списки), в противном случае понятие индекса на самом деле не имеет смысла.
Вот как это выглядит, если вы делаете это шаг за шагом:
>>> 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
- это реализовать свою собственную версию. Вот кое-что, что выполнит более или менее ту же работу, что и zip
но ограничено случаем двух списков (вместо множества итераций):
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
, не является генератором (функцией, которая создает итератор), а вместо этого мы создаем список в памяти. Поэтому эта функция не так хороша, вы можете найти лучшее приближение к настоящему zip
в документации Python. Часто хорошей идеей является прочтение этих эквивалентностей кода, которые есть у вас в этой документации, это хороший способ понять, что функция делает без какой-либо двусмысленности.
Ответ 5
Я предлагаю это, чтобы распаковать zipped список списков, когда zip выполняется с помощью izip_longest:
>>> a =[2,3,4,5,6]
>>> b = [5,4,3,2]
>>> c=[1,0]]
>>>[list([val for val in k if val != None]) for k in
zip(*itertools.izip_longest(a,b,c))]
поскольку izip_longest добавляет None для списков, самый короткий, чем самый длинный, я заранее удаляю None. И я вернусь к оригиналу a, b, c
[[2, 3, 4, 5, 6], [5, 4, 3, 2], [1, 0]]