Звездочка в вызове функции

112

Я использую itertools.chain, чтобы «сгладить» список списков следующим образом:

uniqueCrossTabs = list(itertools.chain(*uniqueCrossTabs))

чем это отличается от слов:

uniqueCrossTabs = list(itertools.chain(uniqueCrossTabs))
Рами
источник
8
Взгляните на распаковку списков аргументов в документации Python для получения дополнительной информации.
Кай
8
вам также следует проверить **оператор - он делает то же самое, что и *с аргументами ключевого слова.
Шон Виейра,

Ответы:

182

* является оператором «splat»: он принимает список в качестве входных данных и расширяет его до фактических позиционных аргументов при вызове функции.

Так что если uniqueCrossTabsбыло [ [ 1, 2 ], [ 3, 4 ] ], то itertools.chain(*uniqueCrossTabs)это то же самое, что сказатьitertools.chain([ 1, 2 ], [ 3, 4 ])

Это явно отличается от передачи просто uniqueCrossTabs. В вашем случае у вас есть список списков, которые вы хотите сгладить; что itertools.chain()возвращает итератор по объединению всех позиционных аргументов, которые вы ему передали, где каждый позиционный аргумент является итеративным сам по себе.

Другими словами, вы хотите передать каждый список в uniqueCrossTabsкачестве аргумента chain(), который объединит их в цепочку, но у вас нет списков в отдельных переменных, поэтому вы используете* оператор, чтобы развернуть список списков на несколько аргументов списка.

Как отметил в комментариях Йохен Ритцель, chain.from_iterable()он лучше подходит для этой операции, так как для начала предполагает наличие единственной итерации из итераций. Тогда ваш код станет просто:

uniqueCrossTabs = list(itertools.chain.from_iterable(uniqueCrossTabs))
Кэмерон
источник
9
@larsmans: Я думаю, что этот термин более популярен в мире Ruby, но он кажется приемлемым и для Python. Мне он нравится, потому что это забавно говорить ;-)
Кэмерон
1
@larsmans: Интересно! Я всегда думал, что это относится к действию распаковки списка в список аргументов, а не к самому персонажу.
Кэмерон
1
Возможно, строки - не лучший пример, потому что не все считают строки итерируемыми. Кстати: Вместо того, chain(*it)чтобы писать chain.from_iterable(it).
Йохен Ритцель 09
@Jochen: Вы абсолютно правы, я заменю его на числа. Кроме того, я даже не знал о from_iterableсуществовании! Я добавлю это в свой ответ в ближайшее время
Кэмерон
1
@Ramy: *только для разбиения списка на позиционные аргументы функции (так что да, очень специфично). Вы можете их for l in uniqueCrossTabs:перебрать. К сожалению, это трудно увидеть *в работе, так как это работает только тогда, когда вы передаете список в функцию (вместо передачи списка в качестве первого параметра *каждый элемент в списке передается как отдельный параметр, один за другим , как если бы они были набраны через запятую в списке параметров)
Кэмерон
72

Он разбивает последовательность на отдельные аргументы для вызова функции.

>>> def foo(a, b=None, c=None):
...   print a, b, c
... 
>>> foo([1, 2, 3])
[1, 2, 3] None None
>>> foo(*[1, 2, 3])
1 2 3
>>> def bar(*a):
...   print a
... 
>>> bar([1, 2, 3])
([1, 2, 3],)
>>> bar(*[1, 2, 3])
(1, 2, 3)
Игнасио Васкес-Абрамс
источник
28

Просто альтернативный способ объяснения концепции / ее использования.

import random

def arbitrary():
    return [x for x in range(1, random.randint(3,10))]

a, b, *rest = arbitrary()

# a = 1
# b = 2
# rest = [3,4,5]
гельбандер
источник
3
это важно и нигде не упоминается
Гершом
1
Этот ответ не относится к вопросу конкретно, но является важным применением звездочки (поэтому я думаю, что это уместно под довольно «туманным» заголовком). В том же духе еще одно важное приложение - определения функций: def func(a, b, *args):см. Этот ответ для получения дополнительной информации.
ASL