Asterisco na chamada de função

111

Estou usando itertools.chain para "nivelar" uma lista de listas desta maneira:

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

como isso é diferente de dizer:

uniqueCrossTabs = list(itertools.chain(uniqueCrossTabs))
Ramy
fonte
8
Dê uma olhada em desempacotar listas de argumentos nos documentos do Python para obter mais informações.
Kai,
8
você também deve verificar o **operador - ele faz a mesma coisa, *mas com argumentos de palavra-chave.
Sean Vieira

Respostas:

181

* é o operador "splat": ele pega uma lista como entrada e a expande em argumentos posicionais reais na chamada de função.

Então, se uniqueCrossTabsfosse [ [ 1, 2 ], [ 3, 4 ] ], itertools.chain(*uniqueCrossTabs)é o mesmo que dizeritertools.chain([ 1, 2 ], [ 3, 4 ])

Isso é obviamente diferente de passar apenas uniqueCrossTabs. No seu caso, você tem uma lista de listas que deseja simplificar; o que itertools.chain()faz é retornar um iterador sobre a concatenação de todos os argumentos posicionais que você passa para ele, onde cada argumento posicional é iterável por si só.

Em outras palavras, você deseja passar cada lista uniqueCrossTabscomo um argumento para chain(), o que as encadeará, mas você não tem as listas em variáveis ​​separadas, então você usa o *operador para expandir a lista de listas em vários argumentos de lista.

Como Jochen Ritzel apontou nos comentários, chain.from_iterable()é mais adequado para esta operação, pois assume um único iterável de iteráveis ​​para começar. Seu código se torna simplesmente:

uniqueCrossTabs = list(itertools.chain.from_iterable(uniqueCrossTabs))
Cameron
fonte
9
@larsmans: Eu acho que o termo é mais popular no mundo Ruby, mas parece ser aceitável para Python também. Eu gosto porque é divertido de dizer ;-)
Cameron,
1
@larsmans: Interessante! Sempre pensei que se referia à ação de desempacotar a lista em uma lista de argumentos, não ao próprio personagem em si.
Cameron,
1
Talvez as strings não sejam o melhor exemplo, porque nem todo mundo vê as strings como iteráveis. A propósito: em vez de chain(*it)escrever chain.from_iterable(it).
Jochen Ritzel,
@Jochen: Você está absolutamente certo, vou mudá-lo para usar números. Além disso, eu nem sabia que from_iterableexistia! Vou adicioná-lo à minha resposta em breve
Cameron,
1
@Ramy: *serve apenas para explodir a lista em argumentos posicionais para uma função (então sim, muito específico). Você pode fazer for l in uniqueCrossTabs:para iterar sobre eles. Infelizmente é difícil ver *em funcionamento, pois só funciona quando você está passando uma lista para uma função (em vez de passar a lista como o primeiro parâmetro, *faz com que cada elemento na lista seja passado como um parâmetro separado, um após o outro , como se tivessem sido digitados separados por vírgulas na lista de parâmetros)
Cameron,
72

Ele divide a sequência em argumentos separados para a chamada de função.

>>> 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)
Ignacio Vazquez-Abrams
fonte
explicação succient com exemplos. +1!
AruniRC de
28

Apenas uma forma alternativa de explicar o conceito / utilizá-lo.

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]
Gelbander
fonte
3
isso é importante e não mencionado em outro lugar
Gershom,
1
Esta resposta não se aplica à pergunta especificamente, mas é uma aplicação importante do asterisco (portanto, acho que é apropriado para o título um tanto "nebuloso"). Na mesma linha, outra aplicação importante é nas definições de funções: def func(a, b, *args):Veja esta resposta para mais informações.
ASL de