Was bedeutet der Sternoperator in einem Funktionsaufruf?

622

Was bedeutet der *Operator in Python, beispielsweise in Code wie zip(*x)oder f(**k)?

  1. Wie wird es intern im Dolmetscher gehandhabt?
  2. Beeinträchtigt es überhaupt die Leistung? Ist es schnell oder langsam?
  3. Wann ist es nützlich und wann nicht?
  4. Sollte es in einer Funktionsdeklaration oder in einem Aufruf verwendet werden?
Psihodelia
quelle
4
Ich denke, dies sollte als "* Funktionsaufrufsyntax" formuliert werden. Sie sind nicht die Betreiber, wenn es verwirrend erhalten , wie es ist ein *und **Betreiber , die nichts mit dieser Syntax zu tun haben.
Ian Bicking
1
@ Ian Bicking: Sie haben volles Recht, * und ** in der Argumentliste sind reine Syntax (Token).
P. Ortiz
25
f(**k)kommt falsch raus wenn du mich fragst :)
Jean-François Fabre
1
Hinweis: Für PEP 448: Zusätzliches Entpacken von Verallgemeinerungen (z. B. [*a, b, *c]oder {**d1, **d2}) möchten Sie Sternchen in Tupel-, Listen- und Set-Definitionen sowie Doppelsternchen in Diktatdefinition lesen , was für die Verwendung außerhalb von Funktionsaufrufen und Funktionsdefinitionen spezifisch ist . Informationen zum früheren PEP 3132 finden Sie unter Zuweisung mehrerer Entpackungen in Python, wenn Sie die Sequenzlänge nicht kennen .
ShadowRanger
1
VTR - Dies ist kein Duplikat von Was tun ** (Doppelstern / Sternchen) und * (Stern / Sternchen) für Parameter? da es sich bei dieser Frage nur um Parameter handelt, obwohl die Antworten auch Funktionsaufrufe abdecken. Das Sternchen im Funktionsaufruf sollte als Duplikat dieser Frage markiert werden, da es weniger beliebt ist und die Top-Antwort weniger vollständig ist.
wjandrea

Antworten:

955

Der einzelne Stern *entpackt die Sequenz / Sammlung in Positionsargumente, sodass Sie Folgendes tun können:

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

values = (1, 2)

s = sum(*values)

Dadurch wird das Tupel entpackt, sodass es tatsächlich wie folgt ausgeführt wird:

s = sum(1, 2)

Der Doppelstern **macht dasselbe, nur unter Verwendung eines Wörterbuchs und damit benannter Argumente:

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

Sie können auch kombinieren:

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

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

wird ausgeführt als:

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

Siehe auch Abschnitt 4.7.4 - Entpacken von Argumentlisten der Python-Dokumentation.


Darüber hinaus können Sie zu verwendende Funktionen *xund **yArgumente definieren. Auf diese Weise kann eine Funktion eine beliebige Anzahl von Positions- und / oder benannten Argumenten akzeptieren, die in der Deklaration nicht speziell benannt sind.

Beispiel:

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

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

oder mit **:

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

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

Auf diese Weise können Sie eine große Anzahl optionaler Parameter angeben, ohne sie deklarieren zu müssen.

Und wieder können Sie kombinieren:

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
quelle
4
Warum brauchen Sie das? Könnte die Funktion nicht einfach über die bereitgestellte Liste iterieren, ohne dass sie erweitert wird?
Martin Beckett
29
Sicher, aber dann müssten Sie es aufrufen: s = sum((1, 2, 3, 4, 5))oder s = sum([1, 2, 3, 4, 5])die *valuesOption lässt den Aufruf so aussehen, als ob er eine Reihe von Argumenten benötigt, aber sie sind in einer Sammlung für den Funktionscode zusammengefasst.
Lasse V. Karlsen
11
Hier ist der eigentliche Vorteil: Sie können Funktionen schreiben, die sonst nicht möglich wären, wenn Sie eine variable Anzahl von Argumenten benötigen. Zum Beispiel ist es schwierig, die printf-Funktion von C mit 1 + n Argumenten als Übung für jeden beginnenden Programmierer zu schreiben. In Python kann ein Anfänger def printf (string_template, * args) schreiben und fortfahren.
IceArdor
1
Was passiert, wenn Sie (aus Versehen vielleicht: p) ein Wörterbuch mit nur einem * anstelle von zwei auspacken? Es scheint etwas zu tun, es scheint, als würde ein Tupel herauskommen, aber es ist nicht so offensichtlich, was es ist. (bearbeiten: ok ich denke die Antwort ist, dass es nur die Schlüssel auspackt, die Werte werden verworfen)
Ben Farmer
1
Das letzte Beispiel impliziert, dass * und ** nicht nur auspacken, sondern auch packen! Siehe diese ausgezeichnete Seite codiername.com/playgrounds/500/…
HCChen
46

Ein kleiner Punkt: Dies sind keine Operatoren. Operatoren werden in Ausdrücken verwendet, um neue Werte aus vorhandenen Werten zu erstellen (1 + 2 wird beispielsweise zu 3). Die Zeichen * und ** sind hier Teil der Syntax von Funktionsdeklarationen und -aufrufen.

Ned Batchelder
quelle
6
Beachten Sie, dass die Python-Dokumentation * in diesem Zusammenhang einen Operator aufruft. Ich stimme zu, das ist irreführend.
Christophe
Vielen Dank. Ich habe nach einer klaren Darstellung in der Python-Referenzdokumentation gesucht und sehe sie immer noch nicht. Die Regel für Funktionsaufrufe lautet also im Grunde, dass ein "*" oder "**", das am Anfang eines Ausdrucks innerhalb eines Funktionsaufrufs steht, diese Art der Erweiterung verursacht.
Nealmcb
20

Ich finde dies besonders nützlich, wenn Sie einen Funktionsaufruf "speichern" möchten.

Angenommen, ich habe einige Komponententests für eine Funktion 'add':

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

Es gibt keine andere Möglichkeit, add aufzurufen, als manuell etwas wie add (test [0], test [1]) auszuführen, was hässlich ist. Wenn es eine variable Anzahl von Variablen gibt, kann der Code mit all den if-Anweisungen, die Sie benötigen, ziemlich hässlich werden.

Ein weiterer nützlicher Ort ist das Definieren von Factory-Objekten (Objekte, die Objekte für Sie erstellen). Angenommen, Sie haben eine Klasse Factory, die Autoobjekte herstellt und zurückgibt. Sie können es so gestalten, dass myFactory.make_car ('rot', 'bmw', '335ix') ein Auto ('rot', 'bmw', '335ix') erstellt und es dann zurückgibt.

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

Dies ist auch nützlich, wenn Sie den Konstruktor einer Oberklasse aufrufen möchten.

Donald Miner
quelle
4
Ich mag deine Beispiele. Aber ich denke -1 + 3 == 2.
eksortso
5
Ich habe absichtlich etwas hineingesteckt, das scheitern würde :)
Donald Miner
19

Es wird als erweiterte Aufrufsyntax bezeichnet. Aus der Dokumentation :

Wenn der Syntax * -Ausdruck im Funktionsaufruf angezeigt wird, muss der Ausdruck zu einer Sequenz ausgewertet werden. Elemente aus dieser Sequenz werden so behandelt, als wären sie zusätzliche Positionsargumente. Wenn es Positionsargumente x1, ..., xN gibt und der Ausdruck eine Folge y1, ..., yM ergibt, entspricht dies einem Aufruf mit M + N Positionsargumenten x1, ..., xN, y1 ,. .., yM.

und:

Wenn der Syntaxausdruck ** im Funktionsaufruf angezeigt wird, muss der Ausdruck zu einer Zuordnung ausgewertet werden, deren Inhalt als zusätzliche Schlüsselwortargumente behandelt wird. Wenn ein Schlüsselwort sowohl im Ausdruck als auch als explizites Schlüsselwortargument vorkommt, wird eine TypeError-Ausnahme ausgelöst.

Mark Byers
quelle
3
Nur eine Fußnote zur Lehrbuchantwort hinzufügen - bevor die syntaktische Unterstützung eintraf, wurde die gleiche Funktionalität mit der eingebauten apply()Funktion erreicht
Jeremy Brown
18

In einem Funktionsaufruf verwandelt der einzelne Stern eine Liste in separate Argumente (z. B. zip(*x)ist das gleiche wie zip(x1,x2,x3)wenn x=[x1,x2,x3]) und der doppelte Stern verwandelt ein Wörterbuch in separate Schlüsselwortargumente (z. B. f(**k)ist das gleiche wie f(x=my_x, y=my_y)wenn) k = {'x':my_x, 'y':my_y}.

In einer Funktionsdefinition ist es umgekehrt: Der einzelne Stern wandelt eine beliebige Anzahl von Argumenten in eine Liste um, und der Doppelstart verwandelt eine beliebige Anzahl von Schlüsselwortargumenten in ein Wörterbuch. ZB def foo(*x)bedeutet "foo nimmt eine beliebige Anzahl von Argumenten und sie sind über die Liste x zugänglich (dh wenn der Benutzer anruft foo(1,2,3), xwird sein [1,2,3])" und def bar(**k)bedeutet "bar nimmt eine beliebige Anzahl von Schlüsselwortargumenten und sie sind über das Wörterbuch k zugänglich (dh wenn der Benutzer anruft bar(x=42, y=23), kwird {'x': 42, 'y': 23}) ".

sepp2k
quelle
3
Ein (sehr) später Kommentar, aber ich glaube von def foo(*x)* x gibt ein Tupel, keine Liste.
Jeremycg