wake-up-neo.com

Was bedeutet der Sternoperator?

Was bedeutet der Operator * In Python, z. B. in Code wie Zip(*x) oder f(**k)?

  1. Wie wird es intern im Interpreter gehandhabt?
  2. Beeinträchtigt es die Leistung überhaupt? Ist es schnell oder langsam?
  3. Wann ist es sinnvoll und wann nicht?
  4. Soll es in einer Funktionsdeklaration oder in einem Aufruf verwendet werden?
540
psihodelia

Der einzelne Stern * entpackt die Sequenz/Sammlung in Positionsargumente, so dass Sie dies tun können:

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

values = (1, 2)

s = sum(*values)

Dadurch wird das Tupel entpackt, so dass es tatsächlich ausgeführt wird als:

s = sum(1, 2)

Der Doppelstern ** tut 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.


Zusätzlich können Sie Funktionen definieren, die *x und **y arguments: Hiermit kann eine Funktion eine beliebige Anzahl von positionsbezogenen 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

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.

40
Ned Batchelder

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

Angenommen, ich habe einige Komponententests für die 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. Auch wenn es eine variable Anzahl von Variablen gibt, könnte der Code mit all den if-Anweisungen, die Sie benötigen würden, 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 Klassenfabrik, die Autoobjekte herstellt und zurückgibt. Sie können festlegen, dass myFactory.make_car ('red', 'bmw', '335ix') Car ('red', 'bmw', '335ix') erstellt und dann zurückgibt.

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

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

17
Donald Miner

Es wird die erweiterte Aufrufsyntax genannt. Aus der Dokumentation :

Wenn der Syntaxausdruck * 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 und expression gibt, die zu einer Sequenz y1, ..., yM ausgewertet werden, 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.

15
Mark Byers

In einem Funktionsaufruf verwandelt der einzelne Stern eine Liste in separate Argumente (z. B. Zip(*x) ist dasselbe wie Zip(x1,x2,x3), wenn x=[x1,x2,x3]) Und der doppelte Stern verwandelt ein Wörterbuch in ein separates Schlüsselwortargumente (z. B. f(**k) ist dasselbe 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 verwandelt eine beliebige Anzahl von Argumenten in eine Liste, und der doppelte Start verwandelt eine beliebige Anzahl von Schlüsselwortargumenten in ein Wörterbuch. Z.B. def foo(*x) bedeutet "foo nimmt eine beliebige Anzahl von Argumenten an und sie sind über die Liste x zugänglich (dh wenn der Benutzer foo(1,2,3) aufruft, ist x[1,2,3]) "Und def bar(**k)" bedeutet, dass die Leiste eine beliebige Anzahl von Schlüsselwortargumenten akzeptiert und über das Wörterbuch k zugänglich ist (dh, wenn der Benutzer bar(x=42, y=23), k wird {'x': 42, 'y': 23}) ".

13
sepp2k