it-swarm.com.de

Warum wird __init __ () immer nach __new __ () aufgerufen?

Ich versuche nur, eine meiner Klassen zu rationalisieren, und habe einige Funktionen in demselben Stil wie das flyweight-Designmuster eingeführt.

Ich bin jedoch etwas verwirrt, warum __init__ immer nach __new__ aufgerufen wird. Ich habe das nicht erwartet. Kann mir jemand sagen, warum das so ist und wie ich diese Funktionalität anders implementieren kann? (Abgesehen von der Implementierung der Implementierung in den __new__, der sich ziemlich hackig 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?

480
Dan

Verwenden Sie _NEW_, wenn Sie .__ steuern möchten. die Erstellung einer neuen Instanz. Benutzen _INIT_, wenn Sie die Initialisierung einer neuen Instanz steuern müssen.

_NEW_ ist der erste Schritt der Instanzerstellung. Es heißt zuerst und ist verantwortlich für die Rücksendung eines neuen Instanz Ihrer Klasse. Im Gegensatz, _INIT_ gibt nichts zurück; es ist nur für die Initialisierung von .__ verantwortlich. Instanz, nachdem es erstellt wurde.

Im Allgemeinen sollten Sie nicht überschreiben Sie _NEW_, es sei denn, Sie sind Unterklasse eines unveränderlichen Typs wie str, int, unicode oder tuple.

Von: http://mail.python.org/pipermail/tutor/2008-April/061426.html

Sie sollten bedenken, dass das, was Sie zu tun versuchen, normalerweise mit einer Factory gemacht wird. Die Verwendung von _NEW_ ist keine gute, saubere Lösung. Bitte berücksichtigen Sie die Verwendung einer Fabrik. Hier haben Sie ein gutes Fabrikbeispiel .

505
mpeterson

__new__ ist eine statische Klassenmethode, während __init__ eine Instanzmethode ist. __new__ muss zuerst die Instanz erstellen, damit __init__ sie initialisieren kann. Beachten Sie, dass __init__self als Parameter verwendet. Bis zum Erstellen einer Instanz gibt es kein self.

Nun versuche ich, dass Sie versuchen, Singleton Pattern in Python zu implementieren. Dafür gibt es mehrere Möglichkeiten.

Ab Python 2.6 können Sie außerdem class decorators verwenden. 

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

@singleton
class MyClass:
  ...
155
vartec

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

In den meisten bekannten Sprachen OO kann der Teil "Attribute der Instanzinstanz initialisieren" für jede Klasse angepasst werden, indem ein Konstruktor definiert wird, der im Grunde nur ein Codeblock ist, der auf die neue Klasse angewendet wird Instanz (mit den für den Konstruktorausdruck bereitgestellten Argumenten), um die gewünschten Anfangsbedingungen festzulegen. In Python entspricht dies der __init__-Methode der Klasse.

Pythons __new__ ist nicht mehr und nicht weniger als eine ähnliche Anpassung der Klasse "Zuordnen einer neuen Instanz" pro Klasse. Auf diese Weise können Sie ungewöhnliche Dinge ausführen, z. B. eine vorhandene Instanz zurückgeben, anstatt eine neue Instanz zuzuweisen. In Python sollten wir diesen Teil nicht unbedingt als notwendigerweise mit Zuteilung betrachten. Alles, was wir benötigen, ist, dass __new__ von irgendwoher eine passende Instanz findet.

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

Oft können Sie einen Refactor durchführen, so dass Sie nur __new__ benötigen, oder Sie brauchen __new__ nicht, oder __init__ verhält sich für ein bereits initialisiertes Objekt anders. Wenn Sie wirklich wollen, können Sie in Python "den Job" neu definieren, so dass SomeClass(arg1, arg2) nicht unbedingt __new__ gefolgt von __init__ aufruft. Dazu müssen Sie eine Metaklasse erstellen und deren __call__-Methode definieren.

Eine Metaklasse ist nur die Klasse einer Klasse. Die __call__-Methode einer Klasse steuert, was passiert, wenn Sie Instanzen der Klasse aufrufen. Eine metaclass '__call__-Methode steuert also, was passiert, wenn Sie eine Klasse aufrufen. Das heißt, Sie können den Instanzerstellungsmechanismus von Anfang bis Ende neu definieren. Dies ist die Ebene, auf der Sie einen vollständig nicht standardmäßigen Instanzerstellungsprozess wie das Singleton-Muster am elegantesten implementieren können. Tatsächlich können Sie mit weniger als 10 Zeilen Code eine Singleton-Metaklasse implementieren, die Sie nicht einmal dazu zwingt, mit __new__überhaupt zu futzeln und any ansonsten normale Klassen zu machen Ein Singleton durch einfaches Hinzufügen von __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 tiefere Magie, als für diese Situation wirklich gerechtfertigt ist!

130
Ben

Um die Dokumentation zu zitieren :

Typische Implementierungen erstellen eine neue Instanz der Klasse durch Aufrufen von Die __new () - Methode der Superklasse verwendet "super (currentclass, cls) .__ new __ (cls [ ...])" mit den entsprechenden Argumenten und dann. Ändern Sie die neu erstellte Instanz nach Bedarf, bevor Sie sie zurückgeben.

...

Wenn __new __ () keine Instanz von cls zurückgibt, dann die neue Die __init __ () - Methode der Instanz wird nicht aufgerufen.

__new __ () soll hauptsächlich Unterklassen von unveränderlichen zulassen Typen (wie int, str oder Tuple) zum Anpassen der Instanzerstellung.

20
tgray

Mir ist klar, dass diese Frage 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

10
Tobias

Wenn __new__ eine Instanz derselben Klasse zurückgibt, wird __init__ für das zurückgegebene Objekt ausgeführt. Das heißt Sie können __new__ NICHT verwenden, um zu verhindern, dass __init__ ausgeführt wird. Selbst wenn Sie ein zuvor erstelltes Objekt aus __new__ zurückgeben, wird es immer wieder doppelt (dreifach usw.) mit __init__ initialisiert.

Hier ist der generische Ansatz des Singleton-Musters, der die vartec-Antwort oben erweitert und korrigiert:

def SingletonClass(cls):
    class Single(cls):
        __doc__ = cls.__doc__
        _initialized = False
        _instance = None

        def __new__(cls, *args, **kwargs):
            if not cls._instance:
                cls._instance = super(Single, cls).__new__(cls, *args, **kwargs)
            return cls._instance

        def __init__(self, *args, **kwargs):
            if self._initialized:
                return
            super(Single, self).__init__(*args, **kwargs)
            self.__class__._initialized = True  # Its crucial to set this variable on the class!
    return Single

Vollständige Geschichte ist hier .

Ein anderer Ansatz, der __new__ beinhaltet, ist die Verwendung von Klassenmethoden:

class Singleton(object):
    __initialized = False

    def __new__(cls, *args, **kwargs):
        if not cls.__initialized:
            cls.__init__(*args, **kwargs)
            cls.__initialized = True
        return cls


class MyClass(Singleton):
    @classmethod
    def __init__(cls, x, y):
        print "init is here"

    @classmethod
    def do(cls):
        print "doing stuff"

Bitte beachten Sie, dass Sie bei diesem Ansatz ALLE Methoden mit @classmethod schmücken müssen, da Sie niemals eine echte Instanz von MyClass verwenden.

8
Zaar Hai

Ich denke, die einfache Antwort auf diese Frage ist, dass, wenn __new__ einen Wert zurückgibt, der denselben Typ wie die Klasse hat, die __init__-Funktion ausgeführt wird. In diesem Fall gibt Ihr CodeA._dict('key')zurück, das dieselbe Klasse wieclsist, daher wird __init__ ausgeführt.

8
PMN
class M(type):
    _dict = {}

    def __call__(cls, key):
        if key in cls._dict:
            print 'EXISTS'
            return cls._dict[key]
        else:
            print 'NEW'
            instance = super(M, cls).__call__(key)
            cls._dict[key] = instance
            return instance

class A(object):
    __metaclass__ = M

    def __init__(self, key):
        print 'INIT'
        self.key = key
        print

a1 = A('aaa')
a2 = A('bbb')
a3 = A('aaa')

ausgänge:

NEW
INIT

NEW
INIT

EXISTS

NB Als Nebeneffekt wird die M._dict-Eigenschaft automatisch von A als A._dict verfügbar gemacht. Achten Sie darauf, sie nicht zufällig zu überschreiben.

5

__new__ sollte eine neue, leere Instanz einer Klasse zurückgeben. __init__ wird dann aufgerufen, um diese Instanz zu initialisieren. Sie rufen nicht __init__ im "NEUEN" Fall von __new__ an, also wird es für Sie angerufen. Der Code, der __new__ aufruft, verfolgt nicht, ob __init__ für eine bestimmte Instanz aufgerufen wurde oder nicht, und sollte dies auch nicht, weil Sie hier etwas sehr Ungewöhnliches tun.

Sie können dem Objekt in der Funktion __init__ ein Attribut hinzufügen, um anzuzeigen, dass es initialisiert wurde. Überprüfen Sie das Vorhandensein dieses Attributs als Erstes in __init__ und fahren Sie nicht weiter fort, wenn dies der Fall ist.

4

Als Antwort auf die Antwort von @AntonyHatchkins möchten Sie wahrscheinlich für jede Klasse des Metatyps ein separates Wörterbuch mit Instanzen. Dies bedeutet, dass Sie eine __init__-Methode in der Metaklasse haben sollten, um Ihr Klassenobjekt mit diesem Wörterbuch zu initialisieren, anstatt es global für alle Klassen zu machen .

class MetaQuasiSingleton(type):
    def __init__(cls, name, bases, attibutes):
        cls._dict = {}

    def __call__(cls, key):
        if key in cls._dict:
            print('EXISTS')
            instance = cls._dict[key]
        else:
            print('NEW')
            instance = super().__call__(key)
            cls._dict[key] = instance
        return instance

class A(metaclass=MetaQuasiSingleton):
    def __init__(self, key):
        print 'INIT'
        self.key = key
        print()

Ich habe den ursprünglichen Code mit einer __init__-Methode aktualisiert und die Syntax in Python 3-Notation geändert (no-arg-Aufruf an super und metaclass in den Klassenargumenten statt als Attribut).

In jedem Fall ist es wichtig, dass Ihr Klasseninitialisierer (__call__-Methode) entweder __new__ oder __init__ nicht ausführt, wenn der Schlüssel gefunden wird. Dies ist viel übersichtlicher als die Verwendung von __new__. Sie müssen das Objekt markieren, wenn Sie den Standardschritt __init__ überspringen möchten.

4
Mad Physicist

Man sollte __init__ als einfachen Konstruktor in traditionellen OO Sprachen betrachten. Wenn Sie beispielsweise mit Java oder C++ vertraut sind, wird dem Konstruktor implizit ein Zeiger auf die eigene Instanz übergeben. Im Falle von Java ist dies die Variable this. Wenn man den für Java generierten Byte-Code untersuchen würde, würde man zwei Aufrufe bemerken. Der erste Aufruf betrifft eine "neue" Methode und der nächste Aufruf die Init-Methode (dh den eigentlichen Aufruf des benutzerdefinierten Konstruktors). Dieser zweistufige Prozess ermöglicht das Erstellen der tatsächlichen Instanz, bevor die Konstruktormethode der Klasse aufgerufen wird, die nur eine andere Methode dieser Instanz ist.

Im Fall von Python ist __new__ eine hinzugefügte Funktion, auf die der Benutzer zugreifen kann. Java bietet diese Flexibilität aufgrund seines typisierten Charakters nicht. Wenn eine Sprache diese Möglichkeit zur Verfügung stellte, könnte der Implementierer von __new__ in dieser Methode viele Dinge tun, bevor er die Instanz zurückgibt, einschließlich der Erstellung einer völlig neuen Instanz eines nicht verbundenen Objekts. Und dieser Ansatz eignet sich besonders für unveränderliche Typen im Fall von Python. 

3
Guru Devanla

Verweis auf dieses Dokument

Bei der Unterklasse unveränderlicher integrierter Typen wie Zahlen und Zeichenfolgen... und gelegentlich in anderen Situationen kommt die statische Methode new praktisch. new ist der erste Schritt in der Instanzkonstruktion, aufgerufen vor init

Die Methode new wird mit der Klasse als .__ aufgerufen. erstes Argument; Ihre Aufgabe ist es, eine neue Instanz von diesem zurückzugeben Klasse. 

Vergleichen Sie dies mit init: init wird mit einer Instanz aufgerufen als erstes Argument, und es gibt nichts zurück; es ist Die Verantwortung liegt darin, die Instanz zu initialisieren. 

Es gibt Situationen wo eine neue Instanz erstellt wird, ohne init aufzurufen (zum Beispiel , wenn die Instanz aus einer Pickle geladen wird). Es gibt keine Möglichkeit, .__ zu erstellen. eine neue Instanz, ohne new aufzurufen (obwohl Sie in einigen Fällen __. mit dem Aufruf von new einer Basisklasse umgehen können).

Zu dem, was Sie erreichen möchten, gibt es auch in derselben Dokumentation Informationen zum Singleton-Muster

class Singleton(object):
        def __new__(cls, *args, **kwds):
            it = cls.__dict__.get("__it__")
            if it is not None:
                return it
            cls.__it__ = it = object.__new__(cls)
            it.init(*args, **kwds)
            return it
        def init(self, *args, **kwds):
            pass

sie können diese Implementierung auch aus PEP 318 mit einem Dekorateur verwenden

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

@singleton
class MyClass:
...
3
octoback

Etwas tiefer hinein zu graben!

Der Typ einer generischen Klasse in CPython ist type und ihre Basisklasse ist Object (es sei denn, Sie definieren explizit eine andere Basisklasse wie eine Metaklasse). Die Reihenfolge der Low-Level-Aufrufe finden Sie hier . Die erste aufgerufene Methode ist type_call, die dann tp_new und dann tp_init aufruft. 

Das Interessante dabei ist, dass tp_new die Object (Basisklasse) neue Methode object_new aufruft, die einen tp_alloc (PyType_GenericAlloc) macht, der den Speicher für das Objekt reserviert :)

Zu diesem Zeitpunkt wird das Objekt im Speicher erstellt und die __init__-Methode wird aufgerufen. Wenn __init__ nicht in Ihrer Klasse implementiert ist, wird object_init aufgerufen, und es wird nichts unternommen :)

Dann gibt type_call nur das Objekt zurück, das an Ihre Variable gebunden ist.

3
xpapazaf

Der __init__ wird nach __new__ aufgerufen, damit der hinzugefügte Code trotzdem aufgerufen wird, wenn Sie ihn in einer Unterklasse überschreiben.

Wenn Sie versuchen, eine Klasse mit einer __new__-Klasse zu subklassifizieren, kann jemand, dem dies nicht bekannt ist, den __init__ anpassen und den Aufruf an die Unterklasse __init__ weiterleiten. Diese Konvention des Aufrufs von __init__ nach __new__ hilft dabei, wie erwartet zu arbeiten.

Der __init__ muss noch alle Parameter berücksichtigen, die von der Superklasse __new__ benötigt werden. Wenn dies nicht geschieht, wird in der Regel ein klarer Laufzeitfehler erzeugt. Und __new__ sollte *args und '** kw' wahrscheinlich explizit zulassen, um deutlich zu machen, dass die Erweiterung in Ordnung ist.

Es ist im Allgemeinen eine schlechte Form, sowohl __new__ als auch __init__ in derselben Klasse auf derselben Vererbungsebene zu haben, da das Originalposter das beschriebene Verhalten aufweist.

1
Jay Obermark

Ich bin jedoch etwas verwirrt, warum init immer nach new aufgerufen wird.

Ich denke, die C++ - Analogie wäre hier nützlich: (A) new ordnet einfach Speicher für das Objekt zu. Die Instanzvariablen eines Objekts benötigen Speicher, um es zu speichern. Dies ist, was der Schritt new tun würde. -. (B) init initialisiert die internen Variablen des Objekts auf bestimmte Werte (dies könnte sein Standard).

1
HAltos

Ich bin jedoch etwas verwirrt, warum __init__ immer nach __new__ aufgerufen wird.

Kein großer Grund, außer dass es nur so gemacht wird. __new__ ist nicht dafür verantwortlich, die Klasse zu initialisieren, andere Methoden (__call__, möglicherweise-- ich weiß es nicht genau).

Ich habe das nicht erwartet. Kann mir jemand sagen, warum das so ist und wie ich diese Funktionalität anders implementiere? (Abgesehen von der Implementierung der Implementierung in den __new__, der sich ziemlich hackig anfühlt).

Sie könnten __init__ nichts tun, wenn es bereits initialisiert wurde, oder Sie könnten eine neue Metaklasse mit einem neuen __call__ schreiben, der nur bei neuen Instanzen __init__ aufruft und ansonsten nur __new__(...) zurückgibt.

0

Jetzt habe ich das gleiche Problem und aus einigen Gründen habe ich mich entschieden, Dekorateure, Fabriken und Metaklassen zu vermeiden. Ich habe es so gemacht:

Hauptdatei

def _alt(func):
    import functools
    @functools.wraps(func)
    def init(self, *p, **k):
        if hasattr(self, "parent_initialized"):
            return
        else:
            self.parent_initialized = True
            func(self, *p, **k)

    return init


class Parent:
    # Empty dictionary, shouldn't ever be filled with anything else
    parent_cache = {}

    def __new__(cls, n, *args, **kwargs):

        # Checks if object with this ID (n) has been created
        if n in cls.parent_cache:

            # It was, return it
            return cls.parent_cache[n]

        else:

            # Check if it was modified by this function
            if not hasattr(cls, "parent_modified"):
                # Add the attribute
                cls.parent_modified = True
                cls.parent_cache = {}

                # Apply it
                cls.__init__ = _alt(cls.__init__)

            # Get the instance
            obj = super().__new__(cls)

            # Push it to cache
            cls.parent_cache[n] = obj

            # Return it
            return obj

Beispielklassen

class A(Parent):

    def __init__(self, n):
        print("A.__init__", n)


class B(Parent):

    def __init__(self, n):
        print("B.__init__", n)

In Benutzung

>>> A(1)
A.__init__ 1  # First A(1) initialized 
<__main__.A object at 0x000001A73A4A2E48>
>>> A(1)      # Returned previous A(1)
<__main__.A object at 0x000001A73A4A2E48>
>>> A(2)
A.__init__ 2  # First A(2) initialized
<__main__.A object at 0x000001A7395D9C88>
>>> B(2)
B.__init__ 2  # B class doesn't collide with A, thanks to separate cache
<__main__.B object at 0x000001A73951B080>
  • Warnung: Sie sollten Parent nicht initialisieren, es würde will mit anderen Klassen kollidieren - es sei denn, Sie haben in jedem der untergeordneten Elemente einen separaten Cache definiert.
  • Warnung: Es scheint eine Klasse mit Parent zu sein, da sich Großeltern seltsam verhalten. [Nicht verifiziert]

Online ausprobieren!

0
RedClover

Der einfache Grund ist, dass new zum Erstellen einer Instanz verwendet wird, während init zum Initialisieren der Instanz verwendet wird. Vor dem Initialisieren sollte die Instanz zuerst erstellt werden. Deshalb sollte new vor init aufgerufen werden.

0
ZQ Hu