Pytanie, które zamierzam zadać, wydaje się być duplikatem użycia przez Pythona funkcji __new__ i __init__? , ale niezależnie od tego, nadal nie jest dla mnie jasne, jaka jest praktyczna różnica między __new__
i __init__
.
Zanim spieszysz się, aby mi powiedzieć, że __new__
służy do tworzenia obiektów i __init__
inicjowania obiektów, pozwól mi wyjaśnić: rozumiem. W rzeczywistości to rozróżnienie jest dla mnie całkiem naturalne, ponieważ mam doświadczenie w C ++, gdzie mamy umieszczenie new , które podobnie oddziela alokację obiektów od inicjalizacji.
Samouczek API Python C wyjaśnia to tak:
Nowy członek jest odpowiedzialny za tworzenie (a nie inicjowanie) obiektów tego typu. Jest ujawniany w Pythonie jako
__new__()
metoda. ... Jednym z powodów wdrożenia nowej metody jest zapewnienie początkowych wartości zmiennych instancji .
Więc tak - rozumiem, co __new__
robi, ale mimo to nadal nie rozumiem, dlaczego jest to przydatne w Pythonie. Podany przykład mówi, że __new__
może to być przydatne, jeśli chcesz „zapewnić początkowe wartości zmiennych instancji”. Cóż, czy to nie jest dokładnie to, co __init__
będzie?
W samouczku interfejsu API języka C pokazano przykład tworzenia nowego typu (zwanego „Noddy”) i __new__
definiowania funkcji Type . Typ Noddy zawiera element członkowski typu string o nazwie first
, a ten element członkowski ciągu jest inicjowany do pustego ciągu, jak poniżej:
static PyObject * Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
.....
self->first = PyString_FromString("");
if (self->first == NULL)
{
Py_DECREF(self);
return NULL;
}
.....
}
Zauważ, że bez __new__
zdefiniowanej tutaj metody musielibyśmy użyć PyType_GenericNew
, która po prostu inicjalizuje wszystkie składowe zmiennej instancji na NULL. Zatem jedyną zaletą tej __new__
metody jest to, że zmienna instancji będzie zaczynać się jako pusty ciąg, w przeciwieństwie do NULL. Ale dlaczego jest to kiedykolwiek przydatne, skoro jeśli zależy nam na upewnieniu się, że zmienne instancji są zainicjowane do jakiejś domyślnej wartości, moglibyśmy to zrobić w __init__
metodzie?
__new__
nie jest boilerplate, ponieważ boilerplate nigdy się nie zmienia. Czasami trzeba zamienić ten konkretny kod na coś innego.