Seite 1 von 2
__new__ vs. __init__
Verfasst: Samstag 31. März 2007, 12:02
von name
Jetzt habe ich mich selbst restlos verwirrt, was ist jetzt genau der Unterschied zwischen __new__ und __init__, herausgefunden hab ich schon, das __new__ eine Instanz vom Typ und __init__ eine von der Klasse erstellt, nur warum wirkt sich das wie folgt aus:
Code: Alles auswählen
class Test(object):
id = 0
def __init__(self):
self.id+=1
print self.id
print "Test"
test = Test()
test1 = Test()
print "--"
class SecondTest(object):
id = 0
def __new__(self):
self.id+=1
print self.id
print "SecondTest"
test = SecondTest()
test1 = SecondTest()
Test
1
1
--
SecondTest
1
2
Verfasst: Samstag 31. März 2007, 12:45
von BlackJack
Der Unterschied ist, das `__new__()` ein echter Konstruktor ist, also ein Objekt erzeugt und zurückgeben muss und `__init__()` nur die Initialisierung eines bereits (z.B. durch `__new__()`) erzeugten Objekts durchführt.
`__new__()` bekommt als erstes Argument die *Klasse* auf der es aufgerufen wird, und `__init__()` ein *Objekt* der Klasse. Deshalb sollte man das Argument bei `__new__()` auch nicht `self` sondern `cls` nennen. Dann wird hoffentlich auch die beobachtete Ausgabe klarer.
Im ersten Fall wird das Attribut von der *Klasse* gelesen, um eins erhöht und an das *Objekt* gebunden. Darum kommt immer 1 heraus.
Im zweiten Fall wird das Attribut von der *Klasse* gelesen, um ein erhöht und auch wieder an die *Klasse* gebunden.
Verfasst: Samstag 31. März 2007, 15:15
von EyDu
BlackJack hat geschrieben:Der Unterschied ist, das `__new__()` ein echter Konstruktor ist, also ein Objekt erzeugt und zurückgeben muss und `__init__()` nur die Initialisierung eines bereits (z.B. durch `__new__()`) erzeugten Objekts durchführt.
Das würde ich so nicht ganz stehen lassen, auch wenn der Begriff Konstruktor sehr unglücklich gewählt wurde. Wie aber auch in eigentlich allen anderen OO-Sprachen versteht man unter einem Konstruktor eine Methode, die ein Objekt nach dessen Erzeugung in einen gültigen Zustand versetzt, mit der Erzeugung hat dieser jedoch nichts zu tun.
Was hier als "echter Konstruktor" bezeichnet wird ist vielmehr eine Fabrik. Da nützt es auch nichts mehr diese ab jetzt mit "Konstruktor" oder "echter Konstruktor" zu versehen, da dieser Begriff einfach schon vergeben ist.
Das wollte ich nur noch mal ergänzen, obwohl ich mir ziehmlich sicher bin, dass BlackJack sich dem durchaus voll bewusst ist, aber sicherlich lesen ja auch noch andere mit

Verfasst: Sonntag 1. April 2007, 17:55
von BlackJack
`__init__()` ist kein Konstruktor sondern eine Initialisierung, die *nach* dem Konstruktor, entweder dem impliziten oder `__new__()`, aufgerufen wird.
Man kann `__init__()` auf einem bestehenden Objekt aufrufen, ohne das ein neues Objekt erzeugt wird. Auch wenn der Quelltext, den der Programmierer in Sprachen wie C++ oder Java für einen Konstruktor schreibt, das Objekt nur initialisiert, der Aufruf des Konstruktors erzeugt ein neues Objekt.
Konstruktor und Fabrik(funktion) sind relativ austauschbare Begriffe in einer Programmiersprache, in der Klassen aufrufbar sind mit der Semantik, dass so ein Aufruf eine Fabrikfunktion für Objekte dieser Klasse darstellt.
Re: __new__ vs. __init__
Verfasst: Freitag 6. Mai 2011, 08:59
von proofy
Wie ist denn so die Zusammenarbeit der beiden Funktionen?
Beispiel:
Code: Alles auswählen
class Project(object):
NumberOfProjects = 0
def __init__(self, inName = None):
if inName:
self.__name = str(inName)
self.__projectNumber = Project.NumberOfProjects
def __new__(cls, *args, **kwds):
cls.NumberOfProjects += 1
instance = super(Project, cls).__new__(cls)
instance.__init__(*args, **kwds)
return instance
def __del__(self):
Project.NumberOfProjects -= 1
Ist das Beispiel so korrekt?
Gibt es beim Destruktor auch zwei Varianten für Klasse und Object?
(Edit: Einrückung korrigiert
Edit 2: Klassen Attribut im init)
Re: __new__ vs. __init__
Verfasst: Freitag 6. Mai 2011, 09:41
von mkesper
proofy hat geschrieben:Ist das Beispiel so korrekt?
Etwas stimmt nicht mit deiner Einrückung!
Re: __new__ vs. __init__
Verfasst: Freitag 6. Mai 2011, 09:50
von lunar
@proofy: Es gibt nur ".__del__()", und das ist kein Destruktor. In Python werden Objekte nie explizit zerstört, mithin kennt die Sprache auch keine Destruktoren so wie es sie in C++ gibt. ".__del__()" wird aufgerufen, bevor das Objekt vom GC zerstört wird, und da der GC als nicht deterministisch angesehen werden muss, ist der Zeitpunkt des Aufrufs von ".__del__()" nicht garantiert. Es ist nicht einmal garantiert, dass ".__del__()" überhaupt unter allen Umständen aufgerufen wird.
Re: __new__ vs. __init__
Verfasst: Freitag 6. Mai 2011, 10:48
von BlackJack
@proofy: Das Beispiel ist nicht korrekt, da die `__init__` zweimal aufgerufen wird.
Zusätzlich zu den Anmerkungen über `__del__()`: Die reine Existenz dieser Methode kann unter bestimmten Umständen verhindern, dass ein Objekt jemals zerstört werden kann. Die Methode ist für reines Python ähnlich sinnlos wie `Object.finalize()` in Java. Man sollte sie meiden. Der einzige sinnvolle Einsatzzweck, den ich bis jetzt gefunden habe, ist bei `ctypes` wenn man Ressourcen freigeben muss, die ausserhalb von Python's Speicherverwaltung liegen.
Sonstige Anmerkungen: Die doppelten Unterstriche bei den Attributen sollte man durch einfache ersetzen. Und der `in`-Prefix bei den Argumenten ist IMHO total sinnfrei.
Re: __new__ vs. __init__
Verfasst: Freitag 6. Mai 2011, 14:50
von proofy
BlackJack hat geschrieben:@proofy: Das Beispiel ist nicht korrekt, da die `__init__` zweimal aufgerufen wird.
Ich wusste ich hab das noch nicht verstanden.
wenn ich also ein Objekt erzeuge mit:
wird automatisch __init__ aufgerufen?
Ist das auch so in IronPython?
BlackJack hat geschrieben:
`__del__()`: Man sollte sie meiden.
Wie kann ich dann wie im Beispiel etwas mit Klassen Attributen machen, wenn das Objekt gelöscht wird?
BlackJack hat geschrieben:Sonstige Anmerkungen: Die doppelten Unterstriche bei den Attributen sollte man durch einfache ersetzen.
Das mache ich für die bessere Anbindung .NET Welt mit Ironpython, wenn nur die definierten Properties erkannt werden soll.
BlackJack hat geschrieben:
Und der `in`-Prefix bei den Argumenten ist IMHO total sinnfrei.
IMHO2, aber es programmieren ja nicht alle mit einer guten IDE

und darum habe ich mir das jetzt so angewöhnt.
Re: __new__ vs. __init__
Verfasst: Freitag 6. Mai 2011, 15:05
von derdon
proofy hat geschrieben:wenn ich also ein Objekt erzeuge mit:
wird automatisch __init__ aufgerufen?
Ist das auch so in IronPython?
Ja, das ist unabhängig von der Implementierung.
proofy hat geschrieben:BlackJack hat geschrieben:Und der `in`-Prefix bei den Argumenten ist IMHO total sinnfrei.
IMHO2, aber es programmieren ja nicht alle mit einer guten IDE

und darum habe ich mir das jetzt so angewöhnt.
Hä? Was ist das für eine komische Begründung? Was hat die Wahl einer IDE mit der Namensgebung der Objekte zu tun? Es ist doch völlig egal, welche IDE oder welcher Editor verwendet wird; auf das Aussehen deines Quelltextes soll das keinen Einfluss haben.
Re: __new__ vs. __init__
Verfasst: Freitag 6. Mai 2011, 15:10
von lunar
@proofy: Man sollte gar nichts machen, wenn ein Objekt gelöscht wird (von einigen Sonderfällen wie ctypes abgesehen). Gerade bei IronPython kannst Du nicht einmal sicher sein, ob ein Objekt zur Laufzeit des Programms überhaupt je gelöscht wird. Wenn Du Dich darauf verlassen musst, oder auch nur darauf reagieren musst, dann ist Dein Quelltext schlicht fehlerhaft.
Um Attribute als privat zu markieren, sollte ein einzelner Unterstrich ausreichen.
Die Begründung zu Typpräfixen bei Namen ist einigermaßen abenteuerlich. Was in Gottes Namen hat denn die IDE damit zu tun?!
Re: __new__ vs. __init__
Verfasst: Freitag 6. Mai 2011, 15:31
von proofy
@derdon
schlecht gefragt von mir,
Wird automatisch erst __init__ aufgerufen und __init__ ruft automatisch vorher __new__ auf?
Oder wird erst versucht __new__ aufzurufen und wenn nicht gefunden dann __init__?
Oder wird erst automatisch __new__ aufgerufen und dann automatisch nachher __init__?
Oder wird immer nur __init__ automatisch aufgerufen?
@lunar und @derdon
Ich bin ja neu bei python und bin doch wirklich manchmal verwundert in was sich die "altgedienten" Python-Programmierer "verbeißen" können, wenn Stichworte wie IDE und Codestyle auftauchen

Aber vielleicht werde ich auch mal so, sobald ich das alles mehr verstanden habe.
Zur einfachen Handhabung, einfach mal meinen Kommentar vergessen ...
Re: __new__ vs. __init__
Verfasst: Freitag 6. Mai 2011, 15:41
von lunar
@proofy: ".__new__()" und ".__init__()" haben per se überhaupt nichts miteinander zu tun. ".__new__()" dient dazu, aus einer Klasse ein Objektexemplar zu erzeugen, und ist daher auch eine Methode einer Klasse. ".__init__()" dagegen ist eine Methode eines Exemplars, und dient dazu, neu erzeugte Exemplare zu initialisieren.
Aufgerufen werden diese Methoden zum entsprechenden Zeitpunkt vom Interpreter.
Re: __new__ vs. __init__
Verfasst: Freitag 6. Mai 2011, 16:31
von BlackJack
@proofy: Betrachten wir mal nur "new style"-Klassen. Da wird immer erst `__new__()` aufgerufen. Wenn man das nicht implementiert, dann halt die von `object`. Wenn `__new__()` ein Exemplar der Klasse liefert auf dem es aufgerufen wurde, wird die `__init__()` auf dem Rückgabewert aufgerufen -- sonst nicht. Steht so in der Dokumentation zu
__new__(). Womit auch geklärt sein sollte wie sich das bei IronPython, Jython, oder sonst einer Python-Implementierung verhält. Wenn es sich "Python" nennt, sollte das dort besser auch so ablaufen.

Re: __new__ vs. __init__
Verfasst: Freitag 6. Mai 2011, 16:55
von proofy
__super__()

ich komm so langsam dahinter, vielen Dank.
Habe das jetzt mal ausprobiert:
Code: Alles auswählen
class Project(object):
NumberOfProjects = 0
def __init__(self, inName = ""):
if inName:
self.__name = str(inName)
self.__projectNumber = Project.NumberOfProjects
print "__init__ in Project %d" % Project.NumberOfProjects
def __new__(cls, *args, **kwds):
instance = super(Project, cls).__new__(cls)
instance.__name = None
instance.__projectNumber = -1
cls.NumberOfProjects += 1
print "__new__ in Project %d" % Project.NumberOfProjects
return instance
def __del__(self):
print "__del__ in Project %d" % self.__projectNumber
Project.NumberOfProjects -= 1
def __repr__(self):
return "Project: \n{\n Name: %s\n ProjectNumber: %d\n }" % (str(self.Name), self.ProjectNumber)
@property
def Name(self):
return self.__name
@property
def ProjectNumber(self):
return self.__projectNumber
p1 = Project("Projekt 1")
p2 = Project("Projekt zwei")
print p1
print p2
del(p2)
p3 = Project("Project III")
print p3
p4 = Project()
print p4
Und habe jetzt wohl dadurch einen weiteren Sinn für __new__ gefunden.
Wenn man sicher gehen will, dass ein Attribut wirklich existieren soll, sollte man es wohl in der __new__ erzeugen und
__del__ ist wirklich böse und darum wohl auch das Runterzählen in __del__
Hier mal die Ausgaben in cpython:
Code: Alles auswählen
bash-3.2$ python klasseverstehen.py
__new__ in Project 1
__init__ in Project 1
__new__ in Project 2
__init__ in Project 2
Project:
{
Name: Projekt 1
ProjectNumber: 1
}
Project:
{
Name: Projekt zwei
ProjectNumber: 2
}
__del__ in Project 2
__new__ in Project 2
__init__ in Project 2
Project:
{
Name: Project III
ProjectNumber: 2
}
__new__ in Project 3
__init__ in Project 3
Project:
{
Name: None
ProjectNumber: 3
}
__del__ in Project 2
__del__ in Project 1
__del__ in Project 3
und in IronPython:
Code: Alles auswählen
bash-3.2$ ipy klasseverstehen.py
__new__ in Project 1
__init__ in Project 1
__new__ in Project 2
__init__ in Project 2
Project:
{
Name: Projekt 1
ProjectNumber: 1
}
Project:
{
Name: Projekt zwei
ProjectNumber: 2
}
__new__ in Project 3
__init__ in Project 3
Project:
{
Name: Project III
ProjectNumber: 3
}
__new__ in Project 4
__del__ in Project 2
__init__ in Project 4
Project:
{
Name: None
ProjectNumber: 4
}
__del__ in Project 4
Re: __new__ vs. __init__
Verfasst: Freitag 6. Mai 2011, 17:58
von cofi
proofy hat geschrieben:Und habe jetzt wohl dadurch einen weiteren Sinn für __new__ gefunden.
Wenn man sicher gehen will, dass ein Attribut wirklich existieren soll, sollte man es wohl in der __new__ erzeugen und
Erm. Und warum sollte es deiner Meinung der Initialisierung in `__init__` ueberlegen sein?
Re: __new__ vs. __init__
Verfasst: Freitag 6. Mai 2011, 18:51
von BlackJack
@proofy: Ich schliesse mich der Frage von cofi an.
Ausserhalb vom Ableiten von "immutable" Typen und Metaprogrammierung fällt mir nur noch das Singleton-Entwurfsmuster als Anwendung ein. Von Metaklassen lasse ich in der Regel die Finger, weil man IMHO mit solcher "Magie" vorsichtig und sparsam umgehen sollte. Von nicht veränderbaren Typen leite ich anscheinend nicht ab, und Singletons setze ich um in dem ich einfach von solchen Klassen nur ein Exemplar erstelle und gut ist. Ich glaube ich habe noch nie in "normalem" Quelltext eine `__new__()`-Methode implementiert. Wenn Du das gewohnheitsmässig machst, oder demnächst machen willst, ist das IMHO ziemlich ungewöhnlich. Denn auch in anderer Leute Quelltext habe ich bisher nur sehr vereinzelt `__new__()`-Methoden gesehen.
Re: __new__ vs. __init__
Verfasst: Freitag 6. Mai 2011, 19:10
von Xynon1
Ich habe __new__ bisher auch nur einmal benutzt
http://www.python-forum.de/viewtopic.php?f=1&t=26077, da ich es dort quasi als Klassen-Fabrik nutze, was man sich auch anders hätte lösen können.
Ansonsten schließe ich mich den Vorednern auch an, da ist nichts *überlegenes* an "__new__". Es hat schlicht einen anderen Aufgabenbereich.
Re: __new__ vs. __init__
Verfasst: Montag 9. Mai 2011, 12:00
von proofy
Klassenfabrik ist z.B. eine gute Gelegenheit. Ich meine ja nicht, dass man es dauernd machen soll.
Bei mir war es z.B. aus json direkt ein Objekt aus einer übergebenen Klasse zu erzeugen.
Auch mit der Kommunikation mit "Fremdsystemen" wie .net scheint es sinnvoll zu sein, weil dort die "Fabriken" gerne __new__ aufrufen als __init__ .
Re: __new__ vs. __init__
Verfasst: Montag 9. Mai 2011, 12:05
von lunar
Für solche Zwecke würde ich eher Klassenmethoden einsetzen, e.g. "Foo.from_json(json)". Explizit, weniger magisch und daher meines Erachtens besser lesbar und leichter verständlich.