137 votes

Python (Python et C de l'API): __new__ rapport __init__

La question que je vais poser semble être un doublon de Python d'utilisation de l' __new__ et __init__ ?, mais peu importe, il est pas encore clair pour moi exactement ce que la pratique, la différence entre __new__ et __init__ .

Avant de vous précipiter pour me dire qu' __new__ est pour la création d'objets et d' __init__ est pour l'initialisation des objets, laissez-moi être clair: je le conçois. En fait, cette distinction est tout à fait naturel pour moi, depuis j'ai de l'expérience en C++, où nous avons le placement de nouveaux, de la même façon qui sépare l'objet de l'allocation de l'initialisation.

Le Python API C tutoriel explique comme ceci:

Le nouveau membre est responsable de l' la création (par opposition à l'initialisation) les objets de ce type. Il est exposé dans Python comme la nouvelleméthode. ... Une raison de mettre en œuvre une nouvelle méthode est de garantir les valeurs initiales de les variables d'instance.

Donc, oui - je obtenir ce qu' __new__ , mais malgré cela, j'ai toujours ne pas comprendre pourquoi il est utile en Python. L'exemple donné dit qu' __new__ peut être utile si vous voulez vous "rassurer les valeurs initiales des variables d'instance". Eh bien, n'est-ce pas exactement ce qu' __init__ va faire?

Dans l'API C tutoriel, un exemple est montré où un nouveau Type (appelé un "oui-oui") est créée, et le Type de l' __new__ fonction est définie. Le Noddy type contient une chaîne de membres appelés first, et cette chaîne membre est initialisé à une chaîne vide comme ceci:

static PyObject * Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    .....

    self->first = PyString_FromString("");
    if (self->first == NULL)
    {
       Py_DECREF(self);
       return NULL;
    }

    .....
}

Notez que sans le __new__ méthode définie ici, nous aurions du utiliser PyType_GenericNew, ce qui initialise simplement tous de la variable d'instance membres à NULL. Donc, le seul avantage de l' __new__ méthode est que la variable d'instance va commencer comme une chaîne vide, par opposition à la valeur NULL. Mais pourquoi est-ce toujours utile, puisque si l'on se souciait de faire en sorte que nos variables d'instance sont initialisés à des valeurs par défaut, nous pourrions en ont fait juste que, dans l' __init__ méthode?

147voto

ncoghlan Points 10779

La différence provient principalement avec mutable vs immuable types.

__new__ accepte un type comme premier argument, et (généralement) retourne une nouvelle instance de ce type. Ainsi, il est adapté pour une utilisation à la fois changeant et immuable types.

__init__ accepte une instance comme premier argument et modifie les attributs de cette instance. C'est inapproprié pour un immuable type, car elle leur permettrait d'être modifié après la création en appelant obj.__init__(*args).

Comparer le comportement de l' tuple et list:

>>> x = (1, 2)
>>> x
(1, 2)
>>> x.__init__([3, 4])
>>> x # tuple.__init__ does nothing
(1, 2)
>>> y = [1, 2]
>>> y
[1, 2]
>>> y.__init__([3, 4])
>>> y # list.__init__ reinitialises the object
[3, 4]

Pourquoi ils sont séparés (à l'exception de simples raisons historiques): __new__ méthodes nécessitent un tas de passe-partout pour obtenir le droit (la création de l'objet initial, puis se souvenant de renvoyer l'objet à la fin). __init__ méthodes, en revanche, sont morts bien simple, depuis que vous venez de définir quels que soient les attributs que vous devez définir.

Hormis __init__ méthodes plus facile à écrire, et la mutable vs immuable distinction indiqué ci-dessus, la séparation peut également être exploité pour faire appel de la classe parente __init__ dans les sous-classes en option par la mise en place de tout absolument nécessaire instance invariants en __new__. Cela est généralement une pratique douteuse cependant - il est généralement plus claire de simplement appeler la classe parente __init__ méthodes que nécessaire.

44voto

senderle Points 41607

Il y a probablement d'autres utilisations pour __new__ mais il n'y a vraiment pas évident: Vous ne pouvez pas sous-classe immuable type sans l'aide d' __new__. Ainsi, par exemple, supposons que vous vouliez créer une sous-classe tuple qui peut contenir que des valeurs entières entre 0 et size.

class ModularTuple(tuple):
    def __new__(cls, tup, size=100):
        tup = (int(x) % size for x in tup)
        return super(ModularTuple, cls).__new__(cls, tup)

Vous simplement ne pouvez pas faire cela avec __init__ -- si vous avez essayé de modifier self en __init__, l'interprète se plaignent de ce que vous essayez de modifier un objet immuable.

40voto

__new__() peut renvoyer des objets de types autres que la classe à laquelle il est lié. __init__() seulement initialise une instance de la classe.

>>> class C(object):
...   def __new__(cls):
...     return 5
...
>>> c = C()
>>> print type(c)
<type 'int'>
>>> print c
5

15voto

Noufal Ibrahim Points 32200

Pas une réponse complète, mais peut-être quelque chose qui illustre la différence.

__new__ toujours quand un objet doit être créé. Il y a certaines situations où l' __init__ ne sera pas appelée. Un exemple est lorsque vous unpickle objets à partir d'un fichier pickle, ils seront attribués (__new__), mais pas initialisé (__init__).

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