Question Pourquoi __init __ () est-il toujours appelé après __new __ ()?


J'essaie juste de rationaliser l'un de mes cours et j'ai introduit certaines fonctionnalités dans le même style que modèle de conception de mouche.

Cependant, je suis un peu confus quant à pourquoi __init__ est toujours appelé après __new__. Je ne m'y attendais pas. Quelqu'un peut-il me dire pourquoi cela se produit et comment je peux mettre en œuvre cette fonctionnalité autrement? (Mis à part mettre la mise en œuvre dans le __new__ qui se sent assez pirate.)

Voici un exemple:

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()

Les sorties:

NEW
INIT

EXISTS
INIT

EXISTS
INIT

Pourquoi?


443
2018-03-23 17:13


origine


Réponses:


Utilisation __Nouveau__ quand vous avez besoin de contrôler   la création d'une nouvelle instance. Utilisation    __init__ lorsque vous devez contrôler l'initialisation d'une nouvelle instance.

__Nouveau__ est la première étape de la création d'instance. Il est appelé en premier, et est   responsable du retour d'un nouveau   instance de votre classe. En revanche,    __init__ ne retourne rien; il est seulement responsable de l'initialisation de la   instance après qu'il a été créé.

En général, vous ne devriez pas avoir besoin de   passer outre __Nouveau__ sauf si vous êtes   sous-classer un type immuable comme   str, int, unicode ou tuple.

De: http://mail.python.org/pipermail/tutor/2008- avril 061426.html

Vous devriez considérer que ce que vous essayez de faire est généralement fait avec un Usine et c'est la meilleure façon de le faire. En utilisant __Nouveau__ n'est pas une bonne solution propre, veuillez donc envisager l'utilisation d'une usine. Vous avez ici un bon exemple d'usine.


471
2018-03-23 17:20



__new__ est la méthode de classe statique, tandis que __init__ est la méthode de l'instance. __new__ doit créer l'instance en premier, donc __init__ peut l'initialiser. Notez que __init__ prend self en tant que paramètre. Jusqu'à ce que vous créez une instance, il n'y a pas de self.

Maintenant, je comprends, que vous essayez de mettre en œuvre motif singleton en Python. Il y a plusieurs façons de le faire.

Aussi, à partir de Python 2.6, vous pouvez utiliser class décorateurs.

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

@singleton
class MyClass:
  ...

144
2017-12-29 07:39



Dans la plupart des langues OO les plus connues, une expression comme SomeClass(arg1, arg2) allouer une nouvelle instance, initialiser les attributs de l'instance, puis le renvoyer.

Dans la plupart des langages OO connus, la partie "initialiser les attributs de l'instance" peut être personnalisée pour chaque classe en définissant un constructeur, qui est fondamentalement juste un bloc de code qui fonctionne sur la nouvelle instance (en utilisant les arguments fournis à l'expression du constructeur) pour définir les conditions initiales souhaitées. En Python, cela correspond à la classe ' __init__ méthode.

Python __new__ n'est rien de plus et rien de moins qu'une personnalisation similaire par classe de la partie "allouer une nouvelle instance". Cela vous permet bien sûr de faire des choses inhabituelles telles que renvoyer une instance existante plutôt que d'en allouer une nouvelle. Donc, en Python, nous ne devrions pas vraiment penser que cette partie implique nécessairement une allocation; tout ce dont nous avons besoin c'est __new__ arrive avec une instance appropriée de quelque part.

Mais ce n'est toujours que la moitié du travail, et le système Python ne sait pas que parfois vous voulez exécuter l'autre moitié du travail (__init__) après et parfois vous ne le faites pas. Si vous voulez ce comportement, vous devez le dire explicitement.

Souvent, vous pouvez refactoriser pour que vous n'ayez besoin que de __new__, ou alors vous n'avez pas besoin __new__, ou alors que __init__ se comporte différemment sur un objet déjà initialisé. Mais si vous voulez vraiment, Python vous permet réellement de redéfinir "le travail", de sorte que SomeClass(arg1, arg2) n'appelle pas nécessairement __new__ suivi par __init__. Pour ce faire, vous devez créer une métaclasse et définir son __call__ méthode.

Une métaclasse n'est que la classe d'une classe. Et une classe ' __call__ méthode contrôle ce qui se passe lorsque vous appelez des instances de la classe. Donc un métaclasse' __call__ méthode contrôle ce qui se passe lorsque vous appelez une classe; c'est-à-dire qu'il vous permet de redéfinir le mécanisme de création d'instance du début à la fin. C'est le niveau auquel vous pouvez le plus élégamment implémenter un processus de création d'instance complètement non standard tel que le modèle singleton. En fait, avec moins de 10 lignes de code, vous pouvez implémenter une Singleton métaclasse qui ne nécessite alors même pas de vous avec __new__  du tout, et peut se tourner tout sinon la classe normale dans un singleton en ajoutant simplement __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

Cependant, c'est probablement une magie plus profonde que ce qui est vraiment justifié pour cette situation!


123
2018-03-23 17:29



Pour citer le Documentation:

Les implémentations typiques créent une nouvelle instance de la classe en appelant   la méthode __new __ () de la superclasse en utilisant "super (currentclass,   cls) .__ new __ (cls [, ...]) "avec des arguments appropriés puis   modifier l'instance nouvellement créée si nécessaire avant de la renvoyer.

...

Si __new __ () ne renvoie pas une instance de cls, alors la nouvelle   La méthode __init __ () de l'instance ne sera pas appelée.

__new __ () vise principalement à permettre des sous-classes d'immuables   types (comme int, str ou tuple) pour personnaliser la création d'instances.


18
2018-05-13 14:12



Je me rends compte que cette question est assez ancienne mais j'ai eu un problème similaire. Ce qui suit a fait ce que je voulais:

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

J'ai utilisé cette page comme ressource http://infohost.nmt.edu/tcc/help/pubs/python/web/new-new-method.html


10
2017-07-07 14:45



Je pense que la réponse simple à cette question est que, si __new__ renvoie une valeur du même type que la classe, le __init__ la fonction s'exécute, sinon ce ne sera pas le cas. Dans ce cas, votre code renvoie A._dict('key') qui est la même classe que cls, alors __init__ sera exécuté.


8
2018-01-26 21:25



Quand __new__ retourne une instance de la même classe, __init__ est exécuté après l'objet retourné. C'est à dire. vous ne pouvez pas utiliser __new__ pour prévenir __init__ d'être couru. Même si vous renvoyez un objet précédemment créé __new__, ce sera double (triple, etc ...) initialisé par __init__ encore et encore.

Voici l'approche générique du pattern Singleton qui étend la réponse vartec ci-dessus et la corrige:

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

L'histoire complète est ici.

Une autre approche, qui implique en fait __new__ est d'utiliser des méthodes de classe:

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"

S'il vous plaît faites attention, avec cette approche, vous devez décorer TOUTES vos méthodes avec @classmethod, parce que vous n'utiliserez jamais une véritable instance de MyClass.


8
2018-03-23 17:23



__new__ devrait renvoyer une nouvelle instance vide d'une classe. __init__ est ensuite appelé pour initialiser cette instance. Vous n'appelez pas __init__ dans le "NOUVEAU" cas de __new__, donc il est appelé pour vous. Le code qui appelle __new__ ne suit pas si __init__ a été appelé sur une instance particulière ou non, et ne le devrait pas, parce que vous faites quelque chose de très inhabituel ici.

Vous pouvez ajouter un attribut à l'objet dans la fonction __init__ pour indiquer qu'il a été initialisé. Vérifiez l'existence de cet attribut en tant que première chose dans __init__ et ne continuez pas si c'est le cas.


4
2017-07-30 12:04



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')

les sorties:

NEW
INIT

NEW
INIT

EXISTS

NB Comme effet secondaire M._dict la propriété devient automatiquement accessible à partir de A comme A._dict Veillez donc à ne pas l'écraser accidentellement.


4
2017-09-30 06:13



En référence à ce document:

En sous-classant les types intégrés immuables comme les nombres et les chaînes,   et occasionnellement dans d'autres situations, la méthode statique Nouveau vient   Pratique. Nouveau est la première étape dans la construction de l'instance, invoquée   avant init.

le Nouveau méthode est appelée avec la classe comme son   premier argument; sa responsabilité est de retourner une nouvelle instance de   classe.

Comparez cela à init: init est appelé avec une instance   comme son premier argument, et il ne retourne rien; ses   La responsabilité est d'initialiser l'instance.

Il y a des situations   où une nouvelle instance est créée sans appel init (par exemple   lorsque l'instance est chargée à partir d'un cornichon). Il n'y a aucun moyen de créer   une nouvelle instance sans appeler Nouveau (bien que dans certains cas, vous pouvez   sortir avec l'appel d'une classe de base Nouveau).

En ce qui concerne ce que vous souhaitez réaliser, il existe également des informations sur le modèle Singleton

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

vous pouvez également utiliser cette implémentation de PEP 318, en utilisant un décorateur

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

@singleton
class MyClass:
...

3
2018-04-24 01:30