__new__ vs. __init__

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
name
User
Beiträge: 254
Registriert: Dienstag 5. September 2006, 16:35
Wohnort: Wien
Kontaktdaten:

__new__ vs. __init__

Beitragvon name » Samstag 31. März 2007, 12:02

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
Ohloh | Mein Blog | Jabber: segfaulthunter@swissjabber.eu | asynchia – asynchrone Netzwerkbibliothek

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.
BlackJack

Beitragvon BlackJack » Samstag 31. März 2007, 12:45

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.
EyDu
User
Beiträge: 4868
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Beitragvon EyDu » Samstag 31. März 2007, 15:15

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 :-)
BlackJack

Beitragvon BlackJack » Sonntag 1. April 2007, 17:55

`__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.
proofy
User
Beiträge: 32
Registriert: Montag 21. März 2011, 12:47

Re: __new__ vs. __init__

Beitragvon proofy » Freitag 6. Mai 2011, 08:59

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)
Zuletzt geändert von proofy am Freitag 6. Mai 2011, 14:37, insgesamt 2-mal geändert.
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Re: __new__ vs. __init__

Beitragvon mkesper » Freitag 6. Mai 2011, 09:41

proofy hat geschrieben:Ist das Beispiel so korrekt?

Etwas stimmt nicht mit deiner Einrückung!
lunar

Re: __new__ vs. __init__

Beitragvon lunar » Freitag 6. Mai 2011, 09:50

@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.
BlackJack

Re: __new__ vs. __init__

Beitragvon BlackJack » Freitag 6. Mai 2011, 10:48

@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.
proofy
User
Beiträge: 32
Registriert: Montag 21. März 2011, 12:47

Re: __new__ vs. __init__

Beitragvon proofy » Freitag 6. Mai 2011, 14:50

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:

Code: Alles auswählen

p1 = Project("Projekt 1")

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.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Re: __new__ vs. __init__

Beitragvon derdon » Freitag 6. Mai 2011, 15:05

proofy hat geschrieben:wenn ich also ein Objekt erzeuge mit:

Code: Alles auswählen

p1 = Project("Projekt 1")

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.
lunar

Re: __new__ vs. __init__

Beitragvon lunar » Freitag 6. Mai 2011, 15:10

@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?!
proofy
User
Beiträge: 32
Registriert: Montag 21. März 2011, 12:47

Re: __new__ vs. __init__

Beitragvon proofy » Freitag 6. Mai 2011, 15:31

@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. :roll:
Zur einfachen Handhabung, einfach mal meinen Kommentar vergessen ...
lunar

Re: __new__ vs. __init__

Beitragvon lunar » Freitag 6. Mai 2011, 15:41

@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.
BlackJack

Re: __new__ vs. __init__

Beitragvon BlackJack » Freitag 6. Mai 2011, 16:31

@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. :-)
proofy
User
Beiträge: 32
Registriert: Montag 21. März 2011, 12:47

Re: __new__ vs. __init__

Beitragvon proofy » Freitag 6. Mai 2011, 16:55

__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

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder