2010-05-24 10 views
6

Mögliche Duplizieren:
least astonishment in python: the mutable default argumentPython Konstruktor tut seltsame Dinge mit optionalen Parametern

Ich will über das Verhalten und die Auswirkungen des Python __init__ Konstruktor verstehen. Wenn ein optionaler Parameter vorhanden ist und Sie versuchen, ein vorhandenes Objekt auf ein neues Objekt zu setzen, wird der optionale Wert des vorhandenen Objekts beibehalten und kopiert.

Blick auf ein Beispiel:

Im folgenden Code Ich versuche, eine Baumstruktur mit Knoten und möglicherweise viele Kinder zu machen. In der ersten Klasse NodeBad hat der Konstruktor zwei Parameter, den Wert und mögliche Kinder. Die zweite Klasse NodeGood übernimmt nur den Wert des Knotens als Parameter. Beide haben eine addchild Methode, um ein Kind zu einem Knoten hinzuzufügen.

Beim Erstellen eines Baums mit der Klasse NodeGood funktioniert es wie erwartet. Wenn Sie jedoch dasselbe mit der Klasse NodeBad machen, scheint es, als ob ein Kind nur einmal hinzugefügt werden kann!

Der folgende Code wird in der folgenden Ausgabe:

Good Tree 
1 
2 
3 
[<3>] 
Bad Tree 
1 
2 
2 
[<2>, <3>] 

Que Pasa? Hier

ist das Beispiel:

#!/usr/bin/python 
class NodeBad: 
    def __init__(self, value, c=[]): 
    self.value = value 
    self.children = c 
    def addchild(self, node): 
    self.children.append(node) 
    def __str__(self): 
    return '< %s >' % self.value 
    def __repr__(self): 
    return '< %s >' % self.value 


class NodeGood: 
    def __init__(self, value): 
    self.value = value 
    self.children = [] 
    def addchild(self, node): 
    self.children.append(node) 
    def __str__(self): 
    return '< %s >' % self.value 
    def __repr__(self): 
    return '< %s >' % self.value 

if __name__ == '__main__': 
    print 'Good Tree' 
    ng = NodeGood(1) # Root Node 
    rootgood = ng 
    ng.addchild(NodeGood(2)) # 1nd Child 
    ng = ng.children[0] 
    ng.addchild(NodeGood(3)) # 2nd Child 

    print rootgood.value 
    print rootgood.children[0].value 
    print rootgood.children[0].children[0].value 
    print rootgood.children[0].children 

    print 'Bad Tree' 
    nb = NodeBad(1) # Root Node 
    rootbad = nb 
    nb.addchild(NodeBad(2)) # 1st Child 
    nb = nb.children[0] 
    nb.addchild(NodeBad(3)) # 2nd Child 

    print rootbad.value 
    print rootbad.children[0].value 
    print rootbad.children[0].children[0].value 
    print rootbad.children[0].children 
+0

Duplikat http://stackoverflow.com/questions/1132941/least-astonishment-in-python -the-mutable-default-Argument – sth

Antwort

12

Das Problem ist, ist der Standardwert eines optionalen Argument nur eine Instanz. Wenn Sie also zum Beispiel def __init__(self, value, c=[]): sagen, wird die gleiche Liste [] immer dann in die Methode übergeben, wenn ein optionales Argument durch Aufruf von Code verwendet wird.

Also im Grunde sollten Sie nur unveränderliche Datumstypen wie None für den Standardwert eines optionalen Arguments verwenden. Zum Beispiel:

def __init__(self, value, c=None): 

Dann könnte man einfach eine neue Liste in der Methode Körper erstellen:

if c == None: 
    c = [] 
+3

Das Problem tritt nur auf, wenn Sie ** veränderliche ** Datentypen als Standardargumente verwenden. Es gibt kein Problem mit irgendwelchen ** unveränderlichen ** Typen (Ganzzahl, Zeichenkette, Tupel usw.) (ich würde es nicht * Konstantenwert * nennen). –

+0

Guter Punkt, ich habe gerade meine Antwort aktualisiert. Vielen Dank! –

+0

Es lohnt sich zu verdeutlichen, dass dies nicht spezifisch für "__init__" oder überhaupt für Klassen ist, sondern für jede Funktion gilt. –