Loop mit __new__

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

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
Zuletzt geändert von Goswin am Montag 16. Februar 2009, 12:04, insgesamt 2-mal geändert.
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

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.
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

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.
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

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
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

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)
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

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?
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

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" :D. Schade, dass die Methode __init__ kein "return" oder "self=..." annimmt; das wird wohl eine Altlast von Python sein.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

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.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

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.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

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 ;-).
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.
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

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 ;)
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

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 :D )
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

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.
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

@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).
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

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.
Zuletzt geändert von snafu am Dienstag 17. Februar 2009, 17:11, insgesamt 1-mal geändert.
Nocta
User
Beiträge: 290
Registriert: Freitag 22. Juni 2007, 14:13

Kann man dann nicht auch einfach die Folge im Falle von ``not folge``als Defaultparameter setzen, anstatt des If-Konstruktes?
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

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.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

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.
Benutzeravatar
nkoehring
User
Beiträge: 543
Registriert: Mittwoch 7. Februar 2007, 17:37
Wohnort: naehe Halle/Saale
Kontaktdaten:

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 ;)
[url=http://www.python-forum.de/post-86552.html]~ Wahnsinn ist auch nur eine andere Form der Intelligenz ~[/url]
hackerkey://v4sw6CYUShw5pr7Uck3ma3/4u7LNw2/3TXGm5l6+GSOarch/i2e6+t2b9GOen7g5RAPa2XsMr2
Antworten