Seite 1 von 1

Unterschied zwischen __init__ und __new__

Verfasst: Freitag 29. August 2014, 20:43
von Papp Nase
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__?

Re: Unterschied zwischen __init__ und __new__

Verfasst: Freitag 29. August 2014, 21:05
von 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.

Re: Unterschied zwischen __init__ und __new__

Verfasst: Freitag 29. August 2014, 21:12
von Papp Nase
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__().

Re: Unterschied zwischen __init__ und __new__

Verfasst: Freitag 29. August 2014, 21:25
von /me
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)

Re: Unterschied zwischen __init__ und __new__

Verfasst: Freitag 29. August 2014, 21:53
von 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.