2010-02-25 7 views
19

Wenn Sie eine Funktion in Python mit einem Array-Parameter definieren, welchen Umfang hat dieser Parameter?Was ist der Umfang eines Standardparameters in Python?

def f(a, L=[]): 
    L.append(a) 
    return L 

print f(1) 
print f(2) 
print f(3) 

Drucke:

Dieses Beispiel aus dem Python-Tutorial genommen

[1] 
[1, 2] 
[1, 2, 3] 

ich nicht ganz sicher bin, ob ich verstehe, was hier passiert. Bedeutet dies, dass der Umfang des Arrays außerhalb der Funktion liegt? Warum speichert das Array seine Werte von Anruf zu Anruf? Wenn ich aus anderen Sprachen komme, würde ich dieses Verhalten nur erwarten, wenn die Variable statisch wäre. Ansonsten scheint es jedes Mal zurückgesetzt zu werden. Und tatsächlich, als ich folgendes probierte:

Ich bekam das Verhalten, das ich erwartete (das Array wurde bei jedem Anruf zurückgesetzt).

So scheint es mir, dass ich nur die Linie def f(a, L=[]): erklärt - was ist der Umfang der L Variable?

+9

Siehe hier: http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument – sth

+1

@sth - danke, die Diskussion dort ist großartig. Diese Zeile erscheint mir sehr beschreibend und hilfreich - "[Python] bindet das Standardargument bei der Funktionsdefinition und nicht bei der Ausführung der Funktion." – charlie

Antwort

17

Der Umfang ist wie erwartet.

Die vielleicht überraschende Sache ist, dass der Standardwert nur einmal berechnet und wiederverwendet wird, so dass jedes Mal, wenn Sie die Funktion aufrufen, die gleiche Liste, keine neue Liste mit [] initialisiert wird.

Die Liste wird in f.func_defaults gespeichert.

def f(a, L=[]): 
    L.append(a) 
    return L 

print f(1) 
print f(2) 
print f(3) 
print f.func_defaults 
f.func_defaults = (['foo'],) # Don't do this! 
print f(4) 

Ergebnis:

[1] 
[1, 2] 
[1, 2, 3] 
([1, 2, 3],) 
['foo', 4] 
+0

Aber das verstehe ich nicht wirklich. Wo ist diese Liste gespeichert? – charlie

+0

@charlie: Das ist eine gute Frage. Ich habe meine Antwort aktualisiert. –

+0

Danke :). Auf Variablen, die innerhalb einer Funktion deklariert sind, kann also nur im Rahmen der Funktion zugegriffen werden, aber Variablen, die innerhalb einer Funktionssignatur/Header/Parameterliste deklariert sind, werden in einen öffentlichen Bereich gestellt und sind überall verfügbar? – charlie

4

Der Umfang der L Variable verhält sich wie erwartet.

Das "Problem" ist mit der Liste, die Sie mit [] erstellen. Python erstellt bei jedem Aufruf der Funktion keine neue Liste. L wird bei jedem Anruf die gleiche Liste zugewiesen, weshalb sich die Funktion bei früheren Anrufen "erinnert".

Also in der Tat ist es das, was Sie haben:

mylist = [] 
def f(a, L=mylist): 
    L.append(a) 
    return L 

Die Python Tutorial puts it this way:

Der Standardwert nur einmal ausgewertet wird. Dies macht einen Unterschied, wenn der Standardwert ein veränderbares Objekt wie eine Liste, ein Wörterbuch oder Instanzen der meisten Klassen ist.

und schlägt die folgende Art und Weise das erwartete Verhalten zu kodieren:

def f(a, L=None): 
    if L is None: 
     L = [] 
    L.append(a) 
    return L 
+0

Das erste Codebeispiel, das Sie geben, ist verwirrend, weil es darauf hinweist, dass der Standardparameter "L" einen globalen Gültigkeitsbereich hat Die Funktion wird definiert. – manifest

+0

@manifest - Ich bin mir nicht sicher, warum du das sagst. Für mich bedeutet das Beispiel, dass jedes Mal, wenn die Funktion aufgerufen wird, "L" ein Wert zugewiesen wird, der nicht jedes Mal neu erstellt wird, wenn die Funktion aufgerufen wird. –

3

Es gibt sogar weniger „Magie“, als Sie vielleicht vermuten. Dies entspricht

m = [] 

def f(a, L=m): 
    L.append(a) 
    return L 

print f(1) 
print f(2) 
print f(3) 

m wird nur einmal erstellt.

+0

Ausgenommen, natürlich ist der Umfang von "m" nicht derselbe wie der von "L" im Original. –

+0

Das ist ein wenig sinnvoller, da die Liste * außerhalb * der Funktion existiert. Daher könnte es sinnvoll sein, dass es bestehen bleibt. Wenn es jedoch innerhalb der Funktion definiert ist, sollte es verloren gehen, sobald Sie es verlassen. Bedeutet das, dass Python die Zeile "def f (a, L = [])" als die 2 Zeilen "L = []" interpretiert und dann "def f (a, L)"? – charlie

+0

Natürlich. @Charlie: Es ist bei der Funktionsdefinition Zeit erstellt, nicht Funktion Ausführungszeit. Funktionen sind auch "nur" Objekte (zugegebenermaßen ein bisschen "speziell", und mit Syntax Zucker, um sie zu erstellen), und wie im anderen Kommentar gezeigt, sind ihre Standard-Parameter in diesem Objekt gespeichert. –

0

Das "Problem" hier ist, dass L=[] nur ausgewertet wird einmal, das heißt, wenn die Datei kompiliert wird. Python durchläuft jede Zeile der Datei und kompiliert sie. Zu dem Zeitpunkt, zu dem es die def mit dem Standardparameter erreicht, erstellt es diese Listeninstanz einmal.

Wenn Sie L = [] in den Funktionscode eingeben, wird die Instanz nicht zur "Kompilierzeit" erstellt (die Kompilierzeit kann auch als Teil der Laufzeit bezeichnet werden), weil Python den Funktionscode kompiliert, aber nicht aufruft. So erhalten Sie eine neue Listeninstanz, da die Erstellung bei jedem Aufruf der Funktion erfolgt (anstatt einmal während der Kompilierung).

Eine Lösung für dieses Problem ist nicht veränderbare Objekte als Standardparameter oder nur feste Instanzen wie None zu verwenden:

def f(a, L = None): 
    if l == None: 
     l = [] 
    L.append(a) 
    return L 

Beachten Sie, dass Sie in beiden Fällen beschrieben, der Umfang der L der Funktionsumfang ist.

0

Die Erklärung wird in Antworten auf this question gegeben. Um es hier zusammenzufassen:

Funktionen in Python sind eine Art von Objekt. Da sie eine Art Objekt sind, verhalten sie sich wie Objekte, wenn sie instanziiert werden. Eine Funktion, die mit einem veränderbaren Attribut als Standardargument definiert ist, entspricht genau einer Klasse mit einem statischen Attribut, bei dem es sich um eine veränderbare Liste handelt.

Lennart Regebro hat a good explanation und die answer to the question von Roberto Liffredo ist ausgezeichnet.

Um Lennart Antwort anzupassen ..., wenn ich eine BananaBunch Klasse:

class BananaBunch: 
    bananas = [] 

    def addBanana(self, banana): 
     self.bananas.append(banana) 


bunch = BananaBunch() 
>>> bunch 
<__main__.BananaBunch instance at 0x011A7FA8> 
>>> bunch.addBanana(1) 
>>> bunch.bananas 
[1] 
>>> for i in range(6): 
    bunch.addBanana("Banana #" + i) 
>>> for i in range(6): 
    bunch.addBanana("Banana #" + str(i)) 

>>> bunch.bananas 
[1, 'Banana #0', 'Banana #1', 'Banana #2', 'Banana #3', 'Banana #4', 'Banana #5'] 

// And for review ... 
//If I then add something to the BananaBunch class ... 
>>> BananaBunch.bananas.append("A mutated banana") 

//My own bunch is suddenly corrupted. :-) 
>>> bunch.bananas 
[1, 'Banana #0', 'Banana #1', 'Banana #2', 'Banana #3', 'Banana #4', 'Banana #5', 'A mutated banana'] 

Wie dies Funktionen gilt? Functions in Python are objects. Dies wiederholt sich. Functions in Python are object s.

Wenn Sie also eine Funktion erstellen, erstellen Sie ein Objekt. Wenn Sie einer Funktion einen veränderbaren Standardwert zuweisen, füllen Sie das Attribut dieses Objekts mit einem veränderbaren Wert, und jedes Mal, wenn Sie diese Funktion aufrufen, arbeiten Sie mit demselben Attribut. Wenn Sie also einen veränderlichen Aufruf (wie append) verwenden, ändern Sie dasselbe Objekt, als ob Sie dem bunch-Objekt Bananen hinzufügen würden.

+0

(Danke für die SO-Link, es ist großartig!) Aber Variablen innerhalb der Funktion deklariert werden sollte auch wie Attribute/Member-Level-Variablen, und sollte auch bestehen bleiben von Anruf zu Anruf. Nein...? – charlie

+0

@charlie, nicht ganz. Das Objekt 'function' enthält ein Attribut für Standardargumente, aber kein Attribut für andere darin deklarierte Variablen. Siehe @Mark Byers für eine ausführlichere Erklärung. –

+0

Auch, down voter, kann ich diesen Beitrag nicht verbessern, wenn ich keine Rückmeldung habe zu * warum * ich falsch liege. ;-) –

-1

Sie müssen bedenken, dass Python eine interpretierte Sprache ist. Was hier passiert ist, wenn die Funktion "f" definiert ist, erstellt sie die Liste und weist sie dem Standardparameter "L" der Funktion "f" zu. Später, wenn Sie diese Funktion aufrufen, wird dieselbe Liste als Standardparameter verwendet. Kurz gesagt, der Code auf der "def" -Zeile wird nur einmal ausgeführt, wenn die Funktion definiert ist. Dies ist eine häufige Python-Falle, in die ich selbst gefallen bin.

def f(a, L=[]): 
    L.append(a) 
    return L 

print f(1) 
print f(2) 
print f(3) 

Es gibt Vorschläge für Idiome in anderen Antworten hier, um dieses Problem zu beheben. Die, die ich vorschlagen würde, ist wie folgt:

def f(a, L=None): 
    L = L or [] 
    L.append(a) 
    return L 

Dies nutzt die oder Kurzschluss entweder den „L“, die verabschiedet wurde, oder eine neue Liste erstellen.

Die Antwort auf Ihre Bereichsfrage ist, dass "L" nur einen Gültigkeitsbereich innerhalb der Funktion "f" hat, aber weil die Standardparameter nur einmal einer einzelnen Liste zugewiesen werden und nicht jedes Mal, wenn Sie die Funktion aufrufen wenn der Standardparameter "L" einen globalen Gültigkeitsbereich hat.

+0

Beachten Sie, dass Sie nur das für Nicht-Literale oder Listen/Dicts/Klassen vorgeschlagene Idiom verwenden müssen – manifest

+0

Das Kurzschließen von 'oder' ist weniger lesbar als eine if-Anweisung explizit ('wenn L nicht ist:'). Wenn ich zum Beispiel eine leere Instanz meines eigenen listenähnlichen Typs übergeben würde, würde ich hoffen, dass 'f' es verwenden würde, aber es würde nicht, obwohl es bei einer nicht leeren Instanz wäre. –

+0

Ah, im Fall von Listen sind Sie richtig. Ich glaube, ich wurde nie damit gebissen, denn wenn ich eine Liste als Parameter übergebe, erwarte ich nicht, sie nach dem Aufruf der Funktion/Methode zu verwenden (wie ein Zeiger in C). Was die Lesbarkeit anbelangt, finde ich sie perfekt lesbar. Wenn Sie jedoch fünf Standardparameter angegeben hätten, würden Sie fünf if-Anweisungen benötigen, die den größten Teil der oberen Hälfte Ihrer Funktion ausfüllen, was für mich einfach hässlich aussieht. Das ist alles natürlich subjektiv. Aber danke für deinen informativen Kommentar. – manifest

1

Sagen Sie bitte den folgenden Code haben:

def func(a=[]): 
    a.append(1) 
    print("A:", a) 

func() 
func() 
func() 

Sie Python Einzug verwenden können, damit Sie verstehen, was los ist. Alles, was am linken Rand liegt, wird ausgeführt, wenn die Datei ausgeführt wird. Alles, was eingerückt ist, wird in ein Code-Objekt kompiliert, das ausgeführt wird, wenn func() aufgerufen wird. Also wird die Funktion definiert und ihre Standardargumente werden einmal gesetzt, wenn das Programm ausgeführt wird (weil die def Anweisung linksbündig ist).

Was es mit den Standardargumenten macht, ist ein interessantes Problem. In Python 3 werden die meisten Informationen zu einer Funktion an zwei Stellen gespeichert: und func.__defaults__. In Python 2 war func.func_codefunc.__defaults__ war func.func_defaults. Spätere Versionen von Python 2, einschließlich 2.6 haben beide Namen, um den Übergang von Python 2 zu Python 3 zu erleichtern. Ich werde die moderneren __code__ und __defaults__ verwenden. Wenn Sie auf einem älteren Python feststecken, sind die Konzepte die gleichen; nur die Namen unterscheiden sich.

Die Standardwerte werden in func.__defaults__ gespeichert und bei jedem Aufruf der Funktion abgerufen.

Wenn Sie also die obige Funktion definieren, wird der Rumpf der Funktion kompiliert und in Variablen unter __code__ gespeichert, um später ausgeführt zu werden, und die Standardargumente werden in __defaults__ gespeichert. Wenn Sie die Funktion aufrufen, werden die Werte in __defaults__ verwendet. Wenn diese Werte aus irgendeinem Grund geändert werden, steht nur die geänderte Version zur Verfügung.

Spielen Sie herum, definieren Sie verschiedene Funktionen im interaktiven Interpreter und sehen Sie, was Sie darüber herausfinden können, wie python Funktionen erstellt und verwendet.

Verwandte Themen