Unterschied zwischen __init__ und __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.
Antworten
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Hi,

ich habe folgendes ausprobiert:

class XYZ:
def __new__():
tu was...
def __init__ (self):
tu was

Beide Funktionen werden beim Aufrufen der Klasse ausgeführt. Wozu dient das __new__?
BlackJack

@Papp Nase: `__new__()` ist ein Konstruktor, der wird als erstes aufgerufen und man kann/muss dort tatsächlich ein Objekt *erstellen*. `__init__()` ist dagegen eine Methode die ein bereits erzeugtes Objekt zum initialisieren übergeben bekommt und die nach `__new__()` aufgerufen wird. Im Grunde brauchst Du `__new__()` erst einmal nicht, denn wenn man nicht irgendetwas ”magisches” Implementieren möchte ist `__init__()` das was man benötigt. Und wenn man ”Magie” implementieren möchte, dann sollte man die Dokumentation von `__new__()` und das Objektmodell von Python schon recht gut verstehen. Das ist eher nichts für Anfänger.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

was könnte denn etwas "magisches" sein? Ich meine, diese Funktion muss doch irgendeinen Zweck erfüllen? Von den Teilen mit den zwei Strichen __...___() gibts ja noch eine ganze Ecke mehr, nicht nur das __init__() und das __new__().
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Papp Nase hat geschrieben:was könnte denn etwas "magisches" sein? Ich meine, diese Funktion muss doch irgendeinen Zweck erfüllen?
Das tut sie ja auch. Wie BlackJack schon sagte dient diese Methode dazu ein Objekt zu erstellen.

Wenn du jetzt noch unbedingt Magie bei der Arbeit sehen willst, dann schau dir den folgenden Code an.

Code: Alles auswählen

    class DomesticAnimal(object):
        def __new__(cls, type_):
            mapper = {'dog': Dog,
                      'cat': Cat,
                      'hund': Dog}

            try:
                cls = mapper[type_.lower()]
            except KeyError:
                raise TypeError('Unknown DomesticAnimal <{0}>!'.format(type_))
            return object.__new__(cls)

        def __init__(self, type_, hitpoints=1):
            self.hitpoints = hitpoints

    class Dog(DomesticAnimal):
        def __init__(self, type_):
            super(Dog, self).__init__(type_)
            self.hitpoints += 9

    class Cat(DomesticAnimal):
        def __init__(self, type_):
            super().__init__(type_)
            self.hitpoints += 6

        pass

    print(type(DomesticAnimal('Hund')))
    print(type(DomesticAnimal('Cat')))
    try:
        print(type(DomesticAnimal('Elefant')))
    except TypeError as e:
        print(e)
BlackJack

@Papp Nase: Nun ja der Zweck ist es eine Möglichkeit zu haben auf die *Erstellung* eines Objekts Einfluss zu nehmen. Das braucht man grundsätzlich weil Objekte ja irgendwann einmal erstellt werden müssen, darum hat `object` so etwas, und wenn man aus irgendwelchen Gründen ein anderes Verhalten möchte als das Standardverhalten an der Stelle, dann kann man das damit machen. Das bezeichne ich als ”magisch” weil das sehr überraschend ist, wie man an /me's Beispiel sieht, denn normalerweise erwartet man ja das man bei ``DomesticAnimal('dog')`` ein Objekt vom Typ `DomesticAnimal` bekommt und nicht eines das ein Untertyp davon ist. So etwas sollte man nur sehr vorsichtig einsetzen. Und für Anfänger sehe ich da keinen Bedarf. Das führt eher zu komischen Sachen.

Die meisten Anfänger kommen mit `__new__` eigentlich nur in Kontakt wenn sie einen Untertyp von einem Typ erstellen wollen dessen Exemplare unveränderbar sind, also zum Beispiel `str`, `int`, `float`, `tuple`, und so weiter, und festellen das sie die `__init__()` von der Basisklasse nicht aufrufen können weil es zu dem Zeitpunkt bereits zu spät ist und man das bereits bestehende Objekt ja nicht mehr verändern kann. Wobei zumindest meine übliche Antwort auf dieses Problem ist, gar nicht erst von solchen Typen zu erben, denn man muss dann in der Regel auch alle Methoden überschreiben die ein Objekt dieses Basistyps zurückgeben um dort dann dafür zu sorgen dass es vom abgeleiteten Typ ist, wo man dann eigentlich auch gleich einen eigenen Typ erstellen und mit Komposition und Delegation arbeiten kann.
Antworten