29 votes

Qu'est-ce que `__init__` peut faire que` __new__` ne peut pas?

En Python, __new__ est utilisé pour initialiser immuable types et __init__ généralement initialise mutable types. Si __init__ ont été retirés de la langue, ce qui ne pouvait plus faire (facilement)?

Par exemple,

class A:

    def __init__(self, *, x, **kwargs):
        super().__init__(**kwargs)
        self.x = x


class B(A):

    def __init__(self, y=2, **kwargs):
        super().__init__(**kwargs)
        self.y = y

Peut être réécrite en utilisant __new__ comme ceci:

class A_N:

    def __new__(cls, *, x, **kwargs):
        obj = super().__new__(cls, **kwargs)
        obj.x = x
        return obj


class B_N(A_N):

    def __new__(cls, y=2, **kwargs):
        obj = super().__new__(cls, **kwargs)
        obj.y = y
        return obj

La Clarification de la portée de la question: Ce n'est pas une question à propos de la façon dont __init__ et __new__ sont utilisés ou quelle est la différence entre eux. C'est une question à propos de ce qui se passerait si __init__ ont été retirés de la langue. Serait rien casser? Serait devenue beaucoup plus difficile, voire impossible, de le faire?

20voto

Andriy Ivaneyko Points 4660

Note à propos de la différence entre l' __new__ et __init__

Avant d'expliquer qu'il manque des fonctionnalités revenons à la définition de l' __new__ et __init__:

__new__ est la première étape de création de l'instance. Il est appelé en premier, et est chargée de renvoyer une nouvelle instance de votre classe.

Toutefois, le __init__ ne retourne rien, c'est seulement responsable pour l'initialisation de l'instance après avoir été créée.

Conséquences du remplacement de __init__ de __new__

Principalement vous viendrait à perdre en flexibilité. Vous obtenez beaucoup de la sémantique des maux de tête et lâche la séparation de initializatin et de la construction (en joignant __new__ andinit nous sont à joindre à la construction et initialisation en une seule étape,...). Jetons un coup d'oeil sur l'extrait de code ci-dessous:

class A(object):
    some_property = 'some_value'

    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls, *args, **kwargs)
        obj.some_property = cls.some_property
        return obj


class B(A):
    some_property = 2

    def __new__(cls, *args, **kwargs):
        obj = super(B, cls).__new__(cls)
        return obj

Les conséquences de déménagement __init__ d'actions en __new__:

  1. Initialiser B avant A: Lorsque vous utilisez __new__ méthode de __init__ votre première étape de création de la nouvelle instance de B est l'appel d' A.__new__ comme effet secondaire vous ne pouvez pas initialiser B avant A est initialisé ( accès et d'attribuer certaines propriétés à nouveau B exemple). À l'aide d' __init__ vous donne une telle flexibilité.

  2. Perdre le contrôle sur l'initialisation de l'ordre: imaginons que vous avez B_N a hérité de deux classes (A_N1, A_N2), maintenant vous manquez de contrôle de la commande d'initialisation de la nouvelle instance de l' B_N(ce qui est l'ordre vous allez initialiser les instances ? il pourrait être question... ce qui est bizarre.)

  3. Propriétés et méthodes de gâchis: vous manquez d'accès à l' A.some_property (cls serait égale à B alors que l'instanciation de la nouvelle instance de l' B. Toutefois accéder directement à de la A.some_property est possible, mais je suppose qu'il est au moins bizarre pour accéder à des propriétés au sein de la classe à travers nom de la classe et non à l'aide de classmethods).

  4. Vous ne pouvez pas re-initialiser une existaient instance sans avoir à créer de l'un ou de mise en œuvre une logique spéciale pour cela ( merci à @platinhom pour l'idée )

Que peut - __init__ faire __new__ ne le peuvent pas?

Il n'y a pas d'actions qui ne peuvent être réalisés en __new__ et ne peut, en __init__, parce que les actions qu' __init__ effectue est un sous-ensemble des actions qui peuvent être effectuées par __new__.

Un moment intéressant de Python Docs, le Décapage et la unpickling normal des instances de classe#objet.getinitargs concernant le moment __init__ pourrait être utile:

Lorsqu'un marinés instance de classe est unpickled, son init() est normalement pas invoquée.

3voto

GingerPlusPlus Points 851

Tout ce que vous pouvez faire en __init__ peut également être fait en __new__ .

Alors, pourquoi utiliser __init__ ?
Parce que vous n'avez pas besoin de stocker l'instance dans une variable ( obj dans votre exemple de code), et plus tard de la renvoyer. Vous pouvez vous concentrer sur ce que vous voulez vraiment faire - initialiser un objet modifiable.

3voto

zangw Points 401

Par When to use __new__ vs. __init__

__new__ est la première étape de création de l'instance. Il est appelé en premier, et est chargée de renvoyer une nouvelle instance de votre classe. Dans contraste, __init__ ne retourne rien, c'est seulement responsable pour l'initialisation de l'instance après avoir été créée.

Aussi la classe de la classe est - type, et type.__call__() est mis en place quelque chose comme ci-dessous reportez-vous à la description ci-dessus:

def __call__(cls, *args, **kwargs):
    obj = cls.__new__(cls, *args, **kwargs)
    if isinstance(obj, cls):
        obj.__init__(*args, **kwargs)
    return obj

Nous savons __init__() il suffit de faire une partie de l' __new__() ne peut rien faire pour cet objet avant que l'objet soit retourné.

En général, vous ne devriez pas avoir besoin de remplacer __new__ sauf si vous êtes sous-classement est immuable type str, int, unicode ou tuple.

Donc, il n'est pas bon de supprimer __init__ de la langue, et il est toujours préférable d'utiliser __init__() de mieux que l'utilisation d' __new__().

Voici une histoire de l' Low-level constructors and __new__().

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X