701 Stimmen

Warum wird __init__() immer nach __new__() aufgerufen?

Ich versuche gerade, eine meiner Klassen zu rationalisieren und habe einige Funktionen im gleichen Stil wie die Fliegengewicht-Designmuster .

Allerdings bin ich ein wenig verwirrt, warum __init__ wird immer aufgerufen nach __new__ . Das habe ich nicht erwartet. Kann mir jemand sagen, warum das passiert und wie ich diese Funktion anders implementieren kann? (Abgesehen davon, dass ich die Implementierung in die __new__ was sich ziemlich hakelig anfühlt.)

Hier ist ein Beispiel:

class A(object):
    _dict = dict()

    def __new__(cls):
        if 'key' in A._dict:
            print "EXISTS"
            return A._dict['key']
        else:
            print "NEW"
            return super(A, cls).__new__(cls)

    def __init__(self):
        print "INIT"
        A._dict['key'] = self
        print ""

a1 = A()
a2 = A()
a3 = A()

Ausgänge:

NEW
INIT

EXISTS
INIT

EXISTS
INIT

Warum?

1 Stimmen

Versuchte, Design Patterns zu verstehen, und hörte zum ersten Mal von :flyweight Design Patterns.. und sehr guter Link mit Beispielen in fast allen gängigen Sprachen.

5 Stimmen

Ist es nicht ein Singleton?

730voto

mpeterson Punkte 743

Utilice __new__ wenn Sie kontrollieren müssen die Erstellung einer neuen Instanz.

Verwenden Sie __init__ wenn Sie die Initialisierung einer neuen Instanz steuern müssen.

__new__ ist der erste Schritt der Instanzerstellung. Er wird als erster aufgerufen, und ist verantwortlich für die Rückgabe einer neuen Instanz Ihrer Klasse zurückzugeben.

Im Gegensatz dazu, __init__ gibt nichts zurück; es ist nur für die Initialisierung der Instanz zu initialisieren, nachdem sie erstellt wurde.

Im Allgemeinen sollte es nicht nötig sein außer Kraft setzen. __new__ es sei denn, Sie sind Unterklasse eines unveränderlichen Typs wie str, int, unicode oder tuple.

Aus dem Beitrag vom April 2008: Wann zu verwenden __new__ vs. __init__ ? auf mail.python.org.

Sie sollten bedenken, dass das, was Sie zu tun versuchen, normalerweise mit einer Fabrik und das ist der beste Weg, es zu tun. Verwendung von __new__ ist keine gute, saubere Lösung, daher sollten Sie den Einsatz einer Fabrik in Betracht ziehen. Hier ist ein gutes Beispiel: AktivStatus F Rezept .

205voto

vartec Punkte 124396

__new__ ist eine statische Klassenmethode, während __init__ ist eine Instanzmethode. __new__ muss zuerst die Instanz erstellen, also __init__ kann ihn initialisieren. Beachten Sie, dass __init__ nimmt self als Parameter. Solange Sie keine Instanz erzeugen, gibt es keine self .

Ich gehe davon aus, dass Sie jetzt versuchen, die Singleton-Muster in Python. Es gibt einige Möglichkeiten, dies zu tun.

Außerdem können Sie seit Python 2.6 die Klasse Dekorateure .

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
  ...

182voto

Ben Punkte 62082

In den meisten bekannten OO-Sprachen ist ein Ausdruck wie SomeClass(arg1, arg2) weist eine neue Instanz zu, initialisiert die Attribute der Instanz und gibt sie dann zurück.

In den meisten bekannten OO-Sprachen kann der Teil "Initialisierung der Attribute der Instanz" für jede Klasse angepasst werden, indem ein Konstrukteur der im Grunde nur ein Codeblock ist, der auf die neue Instanz einwirkt (unter Verwendung der dem Konstruktorausdruck übergebenen Argumente), um die gewünschten Anfangsbedingungen festzulegen. In Python entspricht dies der Klasse __init__ método.

Pythons __new__ ist nicht mehr und nicht weniger als eine ähnliche Anpassung des Teils "Zuweisung einer neuen Instanz" pro Klasse. Dies ermöglicht natürlich auch ungewöhnliche Dinge, wie z. B. die Rückgabe einer vorhandenen Instanz statt der Zuweisung einer neuen Instanz. In Python sollten wir diesen Teil also nicht unbedingt als Allokation betrachten; alles, was wir benötigen, ist, dass __new__ von irgendwoher ein passendes Beispiel findet.

Aber es ist immer noch nur die Hälfte des Jobs, und es gibt keine Möglichkeit für das Python-System zu wissen, dass Sie manchmal die andere Hälfte des Jobs ausführen wollen ( __init__ ) und manchmal auch nicht. Wenn Sie dieses Verhalten wünschen, müssen Sie dies ausdrücklich sagen.

Oft kann man durch Refactoring erreichen, dass man nur noch __new__ oder so brauchen Sie nicht __new__ oder so, dass __init__ verhält sich bei einem bereits initialisierten Objekt anders. Aber wenn Sie es wirklich wollen, erlaubt Python es Ihnen, "den Job" neu zu definieren, so dass SomeClass(arg1, arg2) ruft nicht unbedingt __new__ gefolgt von __init__ . Zu diesem Zweck müssen Sie eine Metaklasse erstellen und ihre __call__ método.

Eine Metaklasse ist einfach die Klasse einer Klasse. Und eine Klasse __call__ Methode steuert, was passiert, wenn Sie Instanzen der Klasse aufrufen. Also eine Metaklasse ' __call__ Methode steuert, was passiert, wenn Sie eine Klasse aufrufen, d.h. sie ermöglicht es Ihnen den Mechanismus der Instanzerstellung von Anfang bis Ende neu zu definieren . Dies ist die Ebene, auf der Sie am elegantesten einen völlig untypischen Instanzerstellungsprozess wie das Singleton-Muster implementieren können. Tatsächlich können Sie mit weniger als 10 Zeilen Code eine Singleton Metaklasse, die es dann nicht einmal erforderlich macht, dass Sie sich mit __new__ überhaupt und können sich tout normale Klasse in ein Singleton verwandeln, indem Sie einfach __metaclass__ = Singleton ¡!

class Singleton(type):
    def __init__(self, *args, **kwargs):
        super(Singleton, self).__init__(*args, **kwargs)
        self.__instance = None
    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            self.__instance = super(Singleton, self).__call__(*args, **kwargs)
        return self.__instance

Dies ist jedoch wahrscheinlich eine tiefere Magie, als sie in dieser Situation wirklich gerechtfertigt ist!

32voto

tgray Punkte 8420

Um die Dokumentation :

Typische Implementierungen erzeugen eine neue Instanz der Klasse durch den Aufruf von der __new__()-Methode der Superklasse mit "super(currentclass, cls).__new__(cls[, ...])" mit den entsprechenden Argumenten aufruft und dann die neu erstellte Instanz nach Bedarf modifizieren, bevor sie zurückgegeben wird.

...

Wenn __new__() keine Instanz von cls zurückgibt, dann wird die neue Instanz die Methode __init__() nicht aufgerufen werden.

__new__() ist hauptsächlich dazu gedacht, Unterklassen von unveränderlichen Typen (wie int, str oder tuple) zu ermöglichen, die Instanzerstellung anzupassen.

14voto

Mir ist klar, dass diese Frage schon ziemlich alt ist, aber ich hatte ein ähnliches Problem. Das Folgende tat, was ich wollte:

class Agent(object):
    _agents = dict()

    def __new__(cls, *p):
        number = p[0]
        if not number in cls._agents:
            cls._agents[number] = object.__new__(cls)
        return cls._agents[number]

    def __init__(self, number):
        self.number = number

    def __eq__(self, rhs):
        return self.number == rhs.number

Agent("a") is Agent("a") == True

Ich habe diese Seite als Ressource verwendet http://infohost.nmt.edu/tcc/help/pubs/python/web/new-new-method.html

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X