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?

13voto

Zaar Hai Punkte 7867

Wenn __new__ gibt eine Instanz der gleichen Klasse zurück, __init__ wird anschließend für das zurückgegebene Objekt ausgeführt. D.h. Sie können NICHT verwenden __new__ zu verhindern __init__ nicht ausgeführt werden. Selbst wenn Sie ein zuvor erstelltes Objekt von __new__ wird es doppelt (dreifach, etc...) initialisiert durch __init__ wieder und wieder.

Hier ist der generische Ansatz für das Singleton-Muster, der die obige Antwort von Vartec erweitert und behebt:

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

Die ganze Geschichte ist ici .

Ein anderer Ansatz, der in der Tat __new__ ist die Verwendung von classmethods:

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 Ihre Methoden mit @classmethod denn Sie werden nie eine echte Instanz von MyClass .

10voto

PMN Punkte 463

Ich denke, die einfache Antwort auf diese Frage ist, dass, wenn __new__ einen Wert zurückgibt, der denselben Typ wie die Klasse hat, wird die __init__ Funktion ausgeführt wird, andernfalls wird sie nicht ausgeführt. In diesem Fall gibt Ihr Code _A._dict('key') die die gleiche Klasse ist wie cls_ also __init__ ausgeführt werden.

8voto

Mad Physicist Punkte 93794

Ein Update zu @AntonyHatchkins Antwort, Sie wollen wahrscheinlich ein separates Wörterbuch von Instanzen für jede Klasse des Metatyps, was bedeutet, dass Sie eine __init__ Methode in der Metaklasse, 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 bin vorgegangen und habe den ursprünglichen Code mit einer __init__ Methode und änderte die Syntax in Python 3-Notation (no-arg-Aufruf an super und Metaklasse in den Klassenargumenten und nicht als Attribut).

Wichtig ist in jedem Fall, dass Ihr Klasseninitialisierer ( __call__ Methode) wird auch nicht ausgeführt __new__ o __init__ wenn der Schlüssel gefunden wird. Dies ist viel sauberer als die Verwendung von __new__ die Sie dazu auffordert, das Objekt zu markieren, wenn Sie die Standardeinstellung überspringen wollen __init__ Schritt.

7voto

HAltos Punkte 575

Allerdings bin ich ein wenig verwirrt, warum __init__ wird immer aufgerufen nach __new__ .

Ich denke, die Analogie zu C++ ist hier nützlich:

  1. __new__ weist einfach Speicher für das Objekt zu. Die Instanzvariablen eines Objekts benötigen Speicher, um sie zu halten, und das ist, was der Schritt __new__ tun würde.

  2. __init__ die internen Variablen des Objekts auf bestimmte Werte zu initialisieren (kann Standard sein).

7voto

Antony Hatchkins Punkte 28362
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 Nebenwirkung M._dict Eigenschaft wird automatisch zugänglich von A como A._dict Achten Sie also darauf, dass Sie es nicht versehentlich überschreiben.

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