Seite 1 von 2
Loop mit __new__
Verfasst: Montag 16. Februar 2009, 11:22
von Goswin
Folgender Code ist falsch (erzeugt unendliche Rekursion), aber warum bloss?
Code: Alles auswählen
class Klob(object):
def __new__(cls,folge=None):
if folge==None: return Klob(folge=31415926535)
else: return Klob(folge=folge)
def __init__(self,folge): self.folge=folge
Verfasst: Montag 16. Februar 2009, 11:37
von audax
Weil ein `Foo()` immer __new__ aufruft und du in __new__ wiederum __new__ aufrufst.
Warum eigentlich nicht so?
Code: Alles auswählen
class Klob(object):
def __init__(self,folge=31415926535): self.folge=folge
Und lass mal das Fett-Schreiben und brüllen, das ist unhöflich.
Verfasst: Montag 16. Februar 2009, 12:01
von Goswin
Ok, vielen Dank!
Ich greife mal zurück auf meine altbewährte factory-Funktion, die lässt sich offenbar nicht so ohne weiteres durch __new__ ersetzen.
Verfasst: Montag 16. Februar 2009, 12:06
von helduel
Moin,
kommt ganz drauf an. Aber Factory-Funktionen sind wohl meistens besser. In __new__ ein nicht offensichtliches Verhalten reinzuprogrammieren kann für Verwirrung sorgen. Und es besteht ja die Chance, dass du die Klasse nochmal wo anders benutzen willst - ohne das spezielle Verhalten. Dann hast du erstmal Pech und musst Code ändern

.
Gruß,
Manuel
Verfasst: Montag 16. Februar 2009, 12:33
von Goswin
Hier ist mir noch ein Trick eingefallen:
Code: Alles auswählen
class Klob(object):
def __init__(self,folge):
assert not folge==None
self.folge=folge
def __repr__(self): return "Klob("+str(self.folge)+")"
#viele_weitere_wichtige_Methoden
_Klob=Klob
class Klob(_Klob):
#verfuegt ueber saemtliche Methoden vom alten Klob
def __new__(cls,folge=None):
if folge==None: return _Klob(folge=31415926535)
else: return _Klob(folge=folge)
Verfasst: Montag 16. Februar 2009, 12:54
von Zap
Ich verstehe nicht was du mit diesem Konstrukt erreichen willst.
Also mich würde zum Beispiel schonmal irritieren
das ich von der neuen Klasse "Klob" keine Instanz bekomme sondern immer
nur von der alten Klasse. Wo ist der Vorteil?
Verfasst: Montag 16. Februar 2009, 13:28
von Goswin
Zap hat geschrieben:Wo ist der Vorteil?
Ganz
meine Frage. Ich habe bisher keine Ahnung, wofür die Methode __new__ gut sein soll, und suche nach Anwendungsbeispielen, die alle verkrampft ausfallen. Aber wie wir schon von Perl wissen: "There-is-more-than-one-way-to-do-it"

. Schade, dass die Methode __init__ kein "return" oder "self=..." annimmt; das wird wohl eine Altlast von Python sein.
Verfasst: Montag 16. Februar 2009, 13:42
von snafu
Ich glaube du denkst einfach nur zu kompliziert. Wenn ein Standard-Wert für "folge" verwendet werden soll (d.h. dieser Wert gilt auch dann, wenn bei der Initialisierung nichts für "folge" angegeben wird), dann macht man das üblicherweise so:
Code: Alles auswählen
class Klob(object):
def __init__(self, folge=31415926535):
self.folge = folge
EDIT: Oh, wurde schon gesagt.
Verfasst: Montag 16. Februar 2009, 13:49
von Leonidas
Goswin hat geschrieben:Ich habe bisher keine Ahnung, wofür die Methode __new__ gut sein soll
Wenn du das Verhalten der Klasse grundlegend ändern willst oder gar etwas anderes Zurückgeben willst oder generell seltsame Sachen.
Und ja, es ist einfach meist nicht nötig, so einfach ist das.
Verfasst: Montag 16. Februar 2009, 13:55
von helduel
Unter "seltsame Sache" könnte man als Beispiel das Singleton-Pattern anführen. Innerhalb von __new__ sorgst du dafür, dass immer dieselbe Instanz zurückgegeben wird. Aber da es eine "seltsame Sache" ist, sollte man sowas nicht tun

.
Verfasst: Montag 16. Februar 2009, 14:08
von lunar
Wtforms nutzt beispielsweise __new__, um die Klassen für Felder zu implementieren. Ein Blick in dessen Quellcode mag zur Aufklärung über mögliche Einsatzzwecke von __new__() dienen.
Verfasst: Montag 16. Februar 2009, 14:16
von Zap
Ich habe lediglich gelesen das es Sinn machen würde um eigenschaften von
Immutable Objects für eigene Zwecke anzupassen.
Mir kommt dabei sowas in den Sinn.
Code: Alles auswählen
In [17]: class Int16(int):
....: def __new__(cls, val):
....: return int.__new__(cls, val, 16)
....:
....: def __str__(self):
....: return "%X" % self
....:
....:
In [18]: Int16("10")
Out[18]: 16
In [19]: print Int16("10")
10
Ob man sowas braucht oder nicht steht auf einem anderen Blatt

Verfasst: Montag 16. Februar 2009, 15:41
von Goswin
snafu hat geschrieben:Ich glaube du denkst einfach nur zu kompliziert.
Vielleicht vereinfache ich meine Beispiele zuviel.
Ich habe komplizierte Objekte, die von verschiedenen Parametern abhängen. Einer dieser Parameter heißt speicherdatei. Bei speicherdatei==None soll das Objekt neu erzeugt werden, was zeitraubend ist. Bei speicherdate<>None wird es mit Hilfe des Pickle-Moduls schnell wiederhergestellt. Um das zu realisieren, benutze ich derzeit eine Fabrikfunktion.
(Meine ursprüngliche Frage wurde von
audax bereits geklärt, wir wären somit hier beim Smalltalk

)
Re: Loop mit __new__
Verfasst: Dienstag 17. Februar 2009, 00:25
von Darii
Goswin hat geschrieben:Folgender Code ist falsch (erzeugt unendliche Rekursion), aber warum bloss?
Wie schon erwähnt wegen dem rekursiven Aufruf.
Versuchs mal so:
Code: Alles auswählen
class Klob(object):
def __new__(cls,folge=None):
if folge==None: return object.__new__(cls, folge=31415926535)
else: return object.__new__(cls, folge=folge)
def __init__(self,folge): self.folge=folge
Für deinen Anwendungsfall ist __new__ schon das richtige Mittel.
Verfasst: Dienstag 17. Februar 2009, 15:56
von Goswin
@Darii:
Es funktioniert NICHT, wenigstens nicht so, wie es sollte. Beobachte einmal, dass man deine Definition von __new__ gänzlich fortlassen kann, ohne dass sich etwas ändert; alle Argumente von "object.__new__" außer dem ersten werden verworfen.
Aber hier habe ich *schwitz* einen weiteren Vorschlag:
Code: Alles auswählen
class Klob(object):
def __new__(cls,folge=31415926535):
self = object.__new__(cls)
#
#Instanzierung hier, abhaengig vom Wert von folge
self.rep = "Klob("+str(folge)+")"
#alle Anweisungen vom ursprünglichen def __init__(self)
#
return self
klob = Klob(folge=123); print klob.rep
klob = Klob(); print klob.rep
Ein mögliches "def __init__(self,folge=None): pass" darf nur dann fortgelassen werden, wenn man __new__ mit einem EINZIGEN Argument aufruft (ansonsten meldet das Programm einen Fehler).
Verfasst: Dienstag 17. Februar 2009, 17:04
von snafu
Ein anderer Vorschlag:
Code: Alles auswählen
In [100]: class Klob(object):
.....: def __init__(self, folge=31415926535):
.....: self.folge = folge
.....: def __str__(self):
.....: return 'Klob(%s)' % self.folge
.....:
.....:
In [101]: klob = Klob()
In [102]: print klob
Klob(31415926535)
In [103]: klob = Klob(folge=123)
In [104]: print klob
Klob(123)
EDIT: Blödsinn durch kurzfristigen Gehirnaussetzer entfernt.
Verfasst: Dienstag 17. Februar 2009, 17:11
von Nocta
Kann man dann nicht auch einfach die Folge im Falle von ``not folge``als Defaultparameter setzen, anstatt des If-Konstruktes?
Verfasst: Dienstag 17. Februar 2009, 17:13
von snafu
Nocta hat geschrieben:Kann man dann nicht auch einfach die Folge im Falle von ``not folge``als Defaultparameter setzen, anstatt des If-Konstruktes?
Natürlich, siehe EDIT. Das mache ich eigentlich auch immer so. War vielleicht verwirrt von den ganzen Konstrukten des OP's. ;P
Das neue Objekt würde ich mir dann übrigens so machen:
Code: Alles auswählen
In [145]: Newklob = lambda: klob
In [146]: newklob = Newklob()
Der Sinn des ganzen bleibt mir weiterhin ein Rätsel...
Goswin hat geschrieben:Bei speicherdatei==None soll das Objekt neu erzeugt werden, was zeitraubend ist. Bei speicherdate<>None wird es mit Hilfe des Pickle-Moduls schnell wiederhergestellt. Um das zu realisieren, benutze ich derzeit eine Fabrikfunktion.
Kann die Fabrikfunktion nicht nach der Prüfung auf Existenz der Speicherdatei direkt entscheiden ob sie das Pickle-File lädt oder das Objekt neu erzeugt? Ich verstehe wie gesagt nicht warum man den Umweg über einen String gehen muss. Wenn du nur den Befehl zur Initialisierung an einen Namen zur späteren Verwendung binden willst, dann nutze - wie gezeigt - ein Lambda-Konstrukt als Rückgabewert der Fabrikfunktion. Natürlich übergibst du lambda dann keinen String sondern direkt den Befehl. Er wird ja dann noch nicht ausgeführt.
Verfasst: Dienstag 17. Februar 2009, 19:32
von Darii
Goswin hat geschrieben:alle Argumente von "object.__new__" außer dem ersten werden verworfen.
Nein, das Problem ist einfach, dass __init__ mit den selben Argumenten wie __new__ aufgerufen wird (siehe Beschreibung von __new__ in der Doku), deswegen gibt es bei Klob() einen Fehler, da __init__ zwei Argumente erwartet.
Verfasst: Mittwoch 18. Februar 2009, 05:48
von nkoehring
Also fuer mich sieht das alles so aus, als wuerde jemand versuchen, javaartig Python zu programmieren, indem er denjenigen, der die Klasse benutzt einschraenkt.
Ich mein, wenn man zur Integritaetssicherung der Klasse unbedingt jeden Wert sichern will, kann man das auch einfach so machen:
Code: Alles auswählen
class Test(object):
def __init__(self, val=345):
self.val = val if isinstance(val, int) else 345
def __repr__(self):
return '<Test with value of %i>' %self.val
Code: Alles auswählen
In [31]: Test()
Out[31]: <Test with value of 345>
In [32]: Test(None)
Out[32]: <Test with value of 345>
In [33]: Test("foo")
Out[33]: <Test with value of 345>
Ich weiß, dass hilft dir jetzt nichts bei der Ideenfindung zum Thema
__new__, aber ich wuerde mich einfach damit abfinden, dass man es wirklich *seltenst* braucht
