2009-06-06 20 views
4

Bevor ich die Kühnheit habe, einen Fehlerbericht zu schreiben, dachte ich, ich würde hier meine Annahmen unter den klügeren Pythonistas überprüfen. Ich traf einen verwirrenden Fall heute, so dass ich schnitzte es zu einem Spielzeug Beispiel unten, unten gezeigt:Python-Funktionsaufrufe sind blutend, stateful, Parameter können nicht initialisiert werden?

#!/usr/bin/env python 
# -*- coding: UTF-8 -*- 

""" 
A little script to demonstrate that a function won't re-initialize its 
list parameters between calls, but instead allows them to retain state. 

""" 

def bleedscope(a=[], b=[]): 
    """ 
    On each call, unless explicitly passed, both `a` and `b` should be 
    initialized as empty lists. 

    """ 

    c = a 
    if b: 
     c.extend(b) 
    return len(c) 


x = bleedscope(b=[1]) 
print x  # Should be 1, as expected. 
x = bleedscope(b=[2]) 
print x  # Expect also to be 1, but it's 2. `a` is retained. 
x = bleedscope(a=[1]) 
print x  # Now 1 as expected. 
x = bleedscope(b=[3]) 
print x  # 1 as expected? No, it's 3! Insanity! 

Ich dachte, Funktionsargumente beschränkt sich auf die Funktion lokal waren und am Ende einer Funktion Garbage Collection Ruf an, niemals den Zustand zwischen ihnen zu halten. Ich habe das obige Skript auf Python 2.5.2 und Python 2.6.1 getestet, und mein Verständnis führt nicht zu den Ergebnissen. Argument a behält sicherlich den Status zwischen den meisten dieser Aufrufe; der am meisten verwirrende ist der letzte Anruf bei bleedscope, wo er den Zustand des vorherigen Anrufs überspringt und zu dem Zustand am Ende des zweiten Anrufs zurückkehrt (d. h. [1, 2]). [Ich schlage vor, dies in Ihrem bevorzugten Debugger auszuführen, um selbst zu sehen. Wenn Sie keinen haben, empfehle ich Winpdb als soliden FOSS Standalone Python Debugger.]

Was ist hier los?

Antwort

15

In Python werden Standardparameterwerte nur initialisiert, wenn der Def-Aufruf geparst wird. Im Falle eines Objekts (z. B. Ihrer Listen) wird es zwischen Anrufen wiederverwendet. Werfen Sie einen Blick auf diese Artikel darüber, die auch die notwendige Abhilfe bietet:

http://effbot.org/zone/default-values.htm

+1

Jedes veränderbare Objekt ist eine schlechte Wahl für einen Standardwert - veränderbare Objekte (wie Listen) können nicht auf diese Weise verwendet werden. –

8

Dies ist Ihr Problem:

def bleedscope(a=[], b=[]): 

es

def bleedscope(a=None, b=None): 
    if a is None: a = [] 
    if b is None: b = [] 

Die Standardparameter sein sollte werden nur einmal ausgeführt, wenn die Funktion geparst wird, also jedes Mal die gleichen 2 Listen.

1

Lustigerweise sind Ihre Eingabe und Ihre Ausgabe aus völlig zufälligen Gründen ziemlich ähnlich.

Was eigentlich mit Python passiert ist, dass die Standardwerte für a und b in Ihrer Methodendeklaration "statische" Werte sind. Sie werden einmal bei der Methodendefinition eingefügt. So wird Ihr Standard "a" jedes Mal gedrückt, wenn Sie kein "a" als Argument übergeben.

Setzen Sie ein "Drucken a" am Anfang Ihrer Methode, um dies zu sehen.

4

Es ist ein FAQ.

+0

Hier ist die aktualisierte [link] (http://docs.python.org/faq/design.html#why-are-default-values-shared-between-objects) –

Verwandte Themen