Gwiazdka w wywołaniu funkcji

111

Używam itertools.chain do „spłaszczenia” listy list w ten sposób:

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

czym różni się to od powiedzenia:

uniqueCrossTabs = list(itertools.chain(uniqueCrossTabs))
Ramy
źródło
8
Aby uzyskać więcej informacji, zapoznaj się z rozpakowywaniem list argumentów w dokumentacji Pythona.
Kai
8
powinieneś również sprawdzić **operator - robi to samo, *ale z argumentami słów kluczowych.
Sean Vieira

Odpowiedzi:

181

* jest operatorem "splat": Pobiera listę jako dane wejściowe i rozwija ją w rzeczywiste argumenty pozycyjne w wywołaniu funkcji.

Więc jeśli uniqueCrossTabsbyło [ [ 1, 2 ], [ 3, 4 ] ], to itertools.chain(*uniqueCrossTabs)znaczy to samo, co powiedzenieitertools.chain([ 1, 2 ], [ 3, 4 ])

To oczywiście różni się od przekazywania po prostu uniqueCrossTabs. W twoim przypadku masz listę list, które chcesz spłaszczyć; co itertools.chain()robi jest powrót iterator nad połączeniem wszystkich pozycyjnych argumentów przekazać do niej, gdzie każdy argument jest iterable pozycyjny w sobie.

Innymi słowy, chcesz przekazać każdą listę uniqueCrossTabsjako argument chain(), który połączy je w łańcuch, ale nie masz list w osobnych zmiennych, więc używasz *operatora, aby rozszerzyć listę list na kilka argumentów list.

Jak zauważył Jochen Ritzel w komentarzach, chain.from_iterable()jest lepiej dostosowany do tej operacji, ponieważ zakłada na początek jedną iterowalną iterowalność. Twój kod staje się wtedy po prostu:

uniqueCrossTabs = list(itertools.chain.from_iterable(uniqueCrossTabs))
Cameron
źródło
9
@larsmans: Myślę, że termin jest bardziej popularny w świecie Rubiego, ale wydaje się, że jest akceptowalny również dla Pythona. Lubię go, bo fajnie to powiedzieć ;-)
Cameron
1
@larsmans: Interesujące! Zawsze myślałem, że odnosi się to do rozpakowywania listy do listy argumentów, a nie do samego znaku.
Cameron
1
Może łańcuchy nie są najlepszym przykładem, ponieważ nie każdy postrzega łańcuchy jako iterowalne. Btw: Zamiast chain(*it)pisać chain.from_iterable(it).
Jochen Ritzel
@Jochen: Masz całkowitą rację, zmienię to, żeby zamiast tego używał liczb. Poza tym nawet nie wiedziałem, że from_iterableistnieje! Wkrótce dodam to do mojej odpowiedzi
Cameron
1
@Ramy: *służy tylko do rozbicia listy na argumenty pozycyjne funkcji (więc tak, bardzo konkretne). Możesz zrobić, for l in uniqueCrossTabs:aby je iterować. Niestety ciężko to zobaczyć *w pracy, ponieważ działa tylko wtedy, gdy przekazujesz listę do funkcji (zamiast przekazywać listę jako pierwszy parametr, *powoduje , że każdy element na liście jest przekazywany jako oddzielny parametr, jeden po drugim , jakby zostały wpisane oddzielone przecinkami w liście parametrów)
Cameron
72

Dzieli sekwencję na oddzielne argumenty dla wywołania funkcji.

>>> 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
źródło
28

Tylko alternatywny sposób wyjaśnienia pojęcia / wykorzystania go.

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
źródło
3
jest to ważne i gdzie indziej nie wspomniano
Gershom
1
Ta odpowiedź nie dotyczy konkretnie pytania, ale jest ważnym zastosowaniem gwiazdki (więc myślę, że jest odpowiednia pod raczej „mglistym” tytułem). W tym samym duchu inna ważna aplikacja dotyczy definicji funkcji: def func(a, b, *args):Zobacz tę odpowiedź, aby uzyskać więcej informacji.
ASL