Python Objektinitialisierung Fehler. Oder bin ich Missverständnis, wie die Arbeit Objekte?
Frage
1 import sys
2
3 class dummy(object):
4 def __init__(self, val):
5 self.val = val
6
7 class myobj(object):
8 def __init__(self, resources):
9 self._resources = resources
10
11 class ext(myobj):
12 def __init__(self, resources=[]):
13 #myobj.__init__(self, resources)
14 self._resources = resources
15
16 one = ext()
17 one._resources.append(1)
18 two = ext()
19
20 print one._resources
21 print two._resources
22
23 sys.exit(0)
Damit wird der Verweis auf die zu one._resources
für beide one
und two
Objekten zugeordnet Objekt drucken. Ich würde denken, dass two
würde ein leeres Array sein, da es eindeutig als solche Einstellung ist, wenn es nicht definiert ist, wenn das Objekt erstellen. Uncommenting myobj.__init__(self, resources)
tut das Gleiche. Mit super(ext, self).__init__(resources)
auch tut das gleiche.
Die einzige Art, wie ich es an der Arbeit bekommen kann, ist, wenn ich die folgende:
two = ext(dummy(2))
Ich sollte nicht manuell einstellen müssen, um den Standardwert, wenn Sie das Objekt erstellen, diese Arbeit zu machen. Vielleicht tue ich. Irgendwelche Gedanken?
Ich habe versucht, dies mit Python 2.5 und 2.6.
Lösung
Sie sollten ändern
def __init__(self, resources=[]):
self._resources = resources
def __init__(self, resources=None):
if resources is None:
resources = []
self._resources = resources
und alles wird besser sein. Dies ist ein Detail in der Art und Weise Standardargumente behandelt werden, wenn sie wandelbar sind. Es gibt einige weitere Informationen in der Diskussion Abschnitt diese Seite .
Andere Tipps
Ihr Problem ist, dass der Standardwert bei Funktionsdefinition Zeit ausgewertet wird. Dies bedeutet, dass das gleiche Liste Objekt zwischen Instanzen gemeinsam genutzt. Siehe Antwort auf diese Frage für weitere Diskussion.
Bitte lesen Sie diese Antwort für eine Diskussion wie eine Klasse von __init__()
einrichten. Sie haben einen bekannten Marotte von Python aufgetreten: Sie versuchen, eine veränderbare einzurichten und Ihre wandelbar ausgewertet wird, sobald wenn __init__()
kompiliert wird. Die Standard Abhilfe ist:
class ext(myobj):
def __init__(self, resources=None):
if resources is None:
resources = []
#myobj.__init__(self, resources)
self._resources = resources
http://docs.python.org/3.1/tutorial/controlflow. html :
Der Standardwert wird nur ausgewertet, Einmal. Das macht einen Unterschied, wenn die Standardwert ist ein veränderliches Objekt wie eine Liste, Wörterbuch oder Instanzen von den meisten Klassen.
Dies ist ein bekannt Python gotcha .
Sie haben auf den Aufruf einer Funktion / Methode unter Verwendung eines veränderliches Objekt zu vermeiden.
Die Objekte, die die Standardwerte bieten nicht zu dem Zeitpunkt erstellt, dass die Funktion / Methode aufgerufen wird, . Sie werden zu dem Zeitpunkt erstellt, dass die Aussage, die die Funktion definiert wird ausgeführt . (Siehe auch die Diskussion unter Standardargumente in Python: zwei einfache Fehler : "Ausdrücke in Standardargumenten berechnet werden, wenn die Funktion definiert ist, nicht, wenn es heißt" )
Dieses Verhalten ist nicht eine Warze in der Sprache Python. Es ist wirklich ein Feature, kein Bug. Es gibt Zeiten, wenn Sie wirklich wandelbar Standardargumente verwenden wollen. Eine Sache, die sie (zum Beispiel) tun können, ist eine Liste von Ergebnissen aus früheren Anrufungen behalten, etwas, das sehr praktisch sein kann.
Aber für die meisten Programmierer - vor allem Pythonistas Anfang - dieses Verhalten ist eine Gotcha. Also für die meisten Fälle, die wir die folgenden Regeln erlassen.
- Sie nie ein veränderbares Objekt verwenden - das ist:. Eine Liste, ein Wörterbuch oder eine Klasseninstanz - als Standardwert eines Arguments
- Ignorieren Regel 1 nur, wenn Sie wirklich, wirklich, wirklich wissen, was Sie tun.
So ... wir planen immer Regel # 1 zu folgen. Nun, die Frage ist, wie es zu tun ... wie man Code Funktion f, um das Verhalten zu bekommen, dass wir wollen.
Zum Glück ist die Lösung einfach. Die veränderbaren Objekte als Standardwert verwendet werden, durch
None
ersetzt, und dann werden die Argumente fürNone
getestet.
Wie kann man es richtig machen? Eine Lösung ist Vermeiden Sie die Verwendung wandelbar Standardwerte für Argumente . Aber das ist kaum zufrieden stellend, wie von Zeit zu Zeit eine neue Liste ein nützlicher Standard ist. Es gibt einige komplexe Lösungen wie ein Dekorateur für Funktionen definieren, die tief kopiert alle Argumente. Dies ist ein viel des Guten, und das Problem kann leicht folgt gelöst werden, wie:
class ext(myobj):
def __init__(self, resources=None):
if not resources:
resources = []
self._resources = resources