Co oznacza operator gwiazdy w wywołaniu funkcji?

631

Co oznacza *operator w Pythonie, na przykład w kodzie, takim jak zip(*x)lub f(**k)?

  1. Jak to jest obsługiwane wewnętrznie przez tłumacza?
  2. Czy to w ogóle wpływa na wydajność? Czy to szybko czy wolno?
  3. Kiedy jest przydatny, a kiedy nie?
  4. Powinien być używany w deklaracji funkcji czy w wywołaniu?
psihodelia
źródło
4
Myślę, że powinno to być sformułowane jako „* składnia wywołania funkcji”. Oni nie są operatorzy, choć będzie się mylące, ponieważ nie jest* i **operatora, że nie mają nic wspólnego z tym składni.
Ian Bicking
1
@Ian Bicking: masz pełną rację, * i ** na liście argumentów to czysta składnia (tokeny).
P. Ortiz
2
Uwaga: W przypadku PEP 448: dodatkowe elementy dotyczące rozpakowywania uogólnień (np. [*a, b, *c]Lub {**d1, **d2}), będziesz chciał czytać gwiazdkę w definicjach krotki, listy i zestawów, podwójną gwiazdkę w definicji dykt , która jest specyficzna dla użycia poza wywołaniami funkcji i definicjami funkcji . W przypadku wcześniejszego PEP 3132 zobacz Przypisanie wielokrotnego rozpakowywania w Pythonie, gdy nie znasz długości sekwencji .
ShadowRanger
1
VTR - to nie jest duplikat Co robi ** (podwójna gwiazdka / gwiazdka) i * (gwiazdka / gwiazdka) dla parametrów? ponieważ to pytanie dotyczy tylko parametrów, chociaż odpowiedzi obejmują również wywołania funkcji. Gwiazdka w wywołaniu funkcji powinna być oznaczona jako duplikat tego pytania, ponieważ jest mniej popularne, a pierwsza odpowiedź jest mniej kompletna.
wjandrea

Odpowiedzi:

968

Pojedyncza gwiazda *rozpakowuje sekwencję / kolekcję na argumenty pozycyjne, więc możesz to zrobić:

def sum(a, b):
    return a + b

values = (1, 2)

s = sum(*values)

Spowoduje to rozpakowanie krotki, aby faktycznie była wykonywana jako:

s = sum(1, 2)

Gwiazda podwójna **robi to samo, używając tylko słownika i nazwanych argumentów:

values = { 'a': 1, 'b': 2 }
s = sum(**values)

Możesz również łączyć:

def sum(a, b, c, d):
    return a + b + c + d

values1 = (1, 2)
values2 = { 'c': 10, 'd': 15 }
s = sum(*values1, **values2)

wykona jako:

s = sum(1, 2, c=10, d=15)

Zobacz także sekcję 4.7.4 - Rozpakowywanie list argumentów w dokumentacji Pythona.


Dodatkowo możesz zdefiniować funkcje do pobrania *xi **yargumenty, dzięki czemu funkcja może zaakceptować dowolną liczbę pozycyjnych i / lub nazwanych argumentów, które nie są wyraźnie wymienione w deklaracji.

Przykład:

def sum(*values):
    s = 0
    for v in values:
        s = s + v
    return s

s = sum(1, 2, 3, 4, 5)

lub z **:

def get_a(**values):
    return values['a']

s = get_a(a=1, b=2)      # returns 1

umożliwia to określenie dużej liczby parametrów opcjonalnych bez konieczności ich deklarowania.

I znowu możesz łączyć:

def sum(*values, **options):
    s = 0
    for i in values:
        s = s + i
    if "neg" in options:
        if options["neg"]:
            s = -s
    return s

s = sum(1, 2, 3, 4, 5)            # returns 15
s = sum(1, 2, 3, 4, 5, neg=True)  # returns -15
s = sum(1, 2, 3, 4, 5, neg=False) # returns 15
Lasse V. Karlsen
źródło
4
dlaczego miałbyś tego potrzebować, czy funkcja nie mogłaby po prostu wykonać iteracji po dostarczonej liście bez jej rozwinięcia?
Martin Beckett
30
Jasne, ale wtedy trzeba by to nazwać: s = sum((1, 2, 3, 4, 5))czy s = sum([1, 2, 3, 4, 5])The *valuesopcja sprawia, że wygląd wywołanie jak to trwa kilka argumentów, ale są one zapakowane w kolekcji dla kodu funkcji.
Lasse V. Karlsen
12
Oto prawdziwa korzyść: możesz pisać funkcje, które w innym przypadku nie byłyby możliwe, gdybyś potrzebował zmiennej liczby argumentów. Na przykład funkcja printf w C, która ma 1 + n argumentów, jest trudna do napisania jako ćwiczenie dla każdego początkującego programisty. W Pythonie początkujący może napisać def printf (string_template, * args) i przejść dalej.
IceArdor
1
Co się stanie, jeśli (być może przypadkowo: p) rozpakujesz słownik zawierający tylko jeden znak * zamiast dwóch? Wydaje się, że coś robi, wydaje się, że wychodzi krotka, ale nie jest tak oczywiste, co to jest. (edycja: ok, myślę, że odpowiedź jest taka, że ​​po prostu rozpakowuje klucze, wartości są odrzucane)
Ben Farmer
1
Ostatni przykład sugeruje, że * i ** nie tylko rozpakowują, ale także pakują! Zobacz tę doskonałą stronę codingame.com/playgrounds/500/…
HCChen
46

Jedna mała uwaga: to nie są operatorzy. Operatory są używane w wyrażeniach do tworzenia nowych wartości z istniejących wartości (na przykład 1 + 2 staje się 3. * I ** tutaj są częścią składni deklaracji funkcji i wywołań).

Ned Batchelder
źródło
7
Zauważ, że dokumentacja Pythona wywołuje * w tym kontekście operatora; Zgadzam się, to trochę mylące.
Christophe
Dzięki. Szukałem jasnego przedstawienia tego w dokumentacji referencyjnej Pythona i nadal tego nie widzę. Zatem reguła dla wywołań funkcji jest taka, że ​​„*” lub „**”, które znajduje się na początku wyrażenia w wywołaniu funkcji, powoduje tego rodzaju rozwinięcie?
nealmcb
21

Uważam, że jest to szczególnie przydatne, gdy chcesz „przechowywać” wywołanie funkcji.

Na przykład załóżmy, że mam kilka testów jednostkowych dla funkcji „dodaj”:

def add(a, b): return a + b
tests = { (1,4):5, (0, 0):0, (-1, 3):3 }
for test, result in tests.items():
   print 'test: adding', test, '==', result, '---', add(*test) == result

Nie ma innego sposobu wywołania add, poza ręcznym wykonaniem czegoś takiego jak add (test [0], test [1]), co jest brzydkie. Ponadto, jeśli istnieje zmienna liczba zmiennych, kod może stać się dość brzydki ze wszystkimi potrzebnymi instrukcjami if.

Innym miejscem, w którym jest to przydatne, jest definiowanie obiektów Factory (obiektów, które tworzą obiekty dla Ciebie). Załóżmy, że masz jakąś klasę Factory, która tworzy obiekty Car i zwraca je. Można to zrobić tak, że myFactory.make_car („red”, „bmw”, „335ix”) tworzy samochód („red”, „bmw”, „335ix”), a następnie zwraca go.

def make_car(*args):
   return Car(*args)

Jest to również przydatne, gdy chcesz wywołać konstruktor nadklasy.

Donald Miner
źródło
4
Podoba mi się twoje przykłady. Ale myślę, że -1 + 3 == 2.
eksortso
5
Celowo umieściłem tam coś, co by się nie udało :)
Donald Miner
19

Nazywa się to rozszerzoną składnią wywołania. Z dokumentacji :

Jeśli wyrażenie składni * pojawia się w wywołaniu funkcji, wyrażenie musi wyliczać na sekwencję. Elementy z tej sekwencji są traktowane tak, jakby były dodatkowymi argumentami pozycyjnymi; jeśli istnieją argumenty pozycyjne x1, ..., xN, a wyrażenie daje w wyniku sekwencję y1, ..., yM, jest to równoważne wywołaniu z M + N argumentami pozycyjnymi x1, ..., xN, y1,. .., yM.

i:

Jeśli w wywołaniu funkcji pojawi się wyrażenie składni **, wyrażenie musi wyliczyć jako odwzorowanie, którego zawartość jest traktowana jako dodatkowe argumenty słów kluczowych. W przypadku, gdy słowo kluczowe występuje zarówno w wyrażeniu, jak i jako jawny argument słowa kluczowego, zgłaszany jest wyjątek TypeError.

Mark Byers
źródło
3
Wystarczy dodać przypis do odpowiedzi podręcznikową - przed składniowej wsparcie przybył, ta sama funkcjonalność została osiągnięta z wbudowaną apply()funkcją
Jeremy Brown
18

W wywołaniu funkcji pojedyncza gwiazdka zamienia listę w osobne argumenty (np. zip(*x)Jest tym samym cozip(x1,x2,x3) if x=[x1,x2,x3]), a podwójna gwiazdka zamienia słownik w osobne argumenty słów kluczowych (np. f(**k)Jest takie samo, jak f(x=my_x, y=my_y)gdybyk = {'x':my_x, 'y':my_y} .

W definicji funkcji jest odwrotnie: pojedyncza gwiazdka zamienia dowolną liczbę argumentów w listę, a podwójny start zamienia dowolną liczbę argumentów słów kluczowych w słownik. Np. def foo(*x)Oznacza "foo przyjmuje dowolną liczbę argumentów i będą one dostępne przez listę x (tj. Jeśli użytkownik wywoła foo(1,2,3), xbędzie [1,2,3])" i def bar(**k)oznacza "bar przyjmuje dowolną liczbę argumentów słów kluczowych i będą one dostępne przez słownik k (tj. jeśli użytkownik dzwoni bar(x=42, y=23), kbędzie {'x': 42, 'y': 23}) ”.

sepp2k
źródło
3
(bardzo) późny komentarz, ale wydaje mi się, że z def foo(*x)* x daje krotkę, a nie listę.
jeremycg