Użyłem, że w Objective-C mam tę konstrukcję:
- (void)init {
if (self = [super init]) {
// init class
}
return self;
}
Czy Python powinien również wywoływać implementację klasy nadrzędnej dla __init__
?
class NewClass(SomeOtherClass):
def __init__(self):
SomeOtherClass.__init__(self)
# init class
Czy to również prawda / fałsz dla __new__()
i __del__()
?
Edycja: Jest bardzo podobne pytanie: dziedziczenie i zastępowanie __init__
w Pythonie
python
oop
superclass
Georg Schölly
źródło
źródło
object
był literówką. Ale teraz nie masz nawetsuper
tytułu swojego pytania, do którego się odnosi.Odpowiedzi:
W Pythonie wywołanie superklasy
__init__
jest opcjonalne. Jeśli to nazwiesz, opcjonalne jest również użyciesuper
identyfikatora lub jawne nazwanie superklasy:W przypadku obiektu wywołanie metody super nie jest bezwzględnie konieczne, ponieważ metoda super jest pusta. To samo dotyczy
__del__
.Z drugiej strony,
__new__
rzeczywiście powinieneś wywołać super metodę i użyć jej zwrotu jako nowo utworzonego obiektu - chyba że jawnie chcesz zwrócić coś innego.źródło
[super init]
, byłaby bardziej powszechna. Tylko spekulacyjna myśl; super konstrukcja w Pythonie 2.x jest dla mnie trochę niezręczna.Jeśli
__init__
oprócz tego, co jest robione w bieżących zajęciach,__init__,
musisz zrobić coś z super zajęć, musisz to nazwać samodzielnie, ponieważ nie stanie się to automatycznie. Ale jeśli nie potrzebujesz niczego od super,__init__,
nie musisz tego nazywać. Przykład:>>> class C(object): def __init__(self): self.b = 1 >>> class D(C): def __init__(self): super().__init__() # in Python 2 use super(D, self).__init__() self.a = 1 >>> class E(C): def __init__(self): self.a = 1 >>> d = D() >>> d.a 1 >>> d.b # This works because of the call to super's init 1 >>> e = E() >>> e.a 1 >>> e.b # This is going to fail since nothing in E initializes b... Traceback (most recent call last): File "<pyshell#70>", line 1, in <module> e.b # This is going to fail since nothing in E initializes b... AttributeError: 'E' object has no attribute 'b'
__del__
jest w ten sam sposób, (ale uważaj na__del__
ostateczność - zamiast tego rozważ zrobienie tego za pomocą instrukcji with).Rzadko używam
__new__.
, wykonuję całą inicjalizację w__init__.
źródło
super(D,self).__init__()
W odpowiedzi Anona:
„Jeśli potrzebujesz czegoś z superumiejętności
__init__
do zrobienia oprócz tego, co jest robione w bieżącej klasie__init__
, musisz to nazwać sam, ponieważ nie stanie się to automatycznie”To niewiarygodne: mówi dokładnie przeciwnie do zasady dziedziczenia.
Nie chodzi o to, że "coś z super
__init__
(...) nie wydarzy się automatycznie" , chodzi o to, że BYŁO to automatycznie, ale nie dzieje się tak, ponieważ klasa bazowa '__init__
jest nadpisana przez definicję klas pochodnych__init__
Więc DLACZEGO definiowanie klasy pochodnej
__init__
, skoro zastępuje to, do czego jest skierowane, gdy ktoś ucieka się do dziedziczenia?Dzieje się tak, ponieważ trzeba zdefiniować coś, czego NIE robi się w klasie bazowej '
__init__
, a jedyną możliwością uzyskania tego jest umieszczenie jej wykonania w__init__
funkcji klasy pochodnej' .Innymi słowy, potrzeba czegoś w klasie bazowej „
__init__
oprócz tego, co zostałoby zrobione automatycznie w klasie podstawowej”,__init__
gdyby ta ostatnia nie została zastąpiona.NIE przeciwnie.
Następnie problem polega na tym, że pożądane instrukcje obecne w klasie bazowej '
__init__
nie są już aktywowane w momencie ich tworzenia. Aby zrównoważyć tę dezaktywację, wymagane jest coś specjalnego: jawne wywołanie klasy bazowej „__init__
w celu ZACHOWANIA , NIE DODAWANIA, inicjalizacji wykonywanej przez klasę bazową”__init__
. Dokładnie to jest powiedziane w oficjalnym dokumencie:To cała historia:
gdy celem jest ZACHOWANIE inicjalizacji wykonywanej przez klasę bazową, czyli czyste dziedziczenie, nic specjalnego nie jest potrzebne, należy po prostu unikać definiowania
__init__
funkcji w klasie pochodnejgdy celem jest ZASTĄPIENIE inicjalizacji wykonywanej przez klasę bazową,
__init__
musi być zdefiniowana w klasie pochodnejgdy celem jest dodanie procesów do inicjalizacji wykonywanej przez klasę bazową,
__init__
należy zdefiniować klasę pochodną , zawierającą jawne wywołanie klasy bazowej__init__
To, co czuję zdumiewające na stanowisku Anona, to nie tylko to, że wyraża on przeciwieństwo teorii dziedziczenia, ale że było 5 facetów, którzy przeszli obok tego głosowania bez odwracania włosów, a ponadto nie było nikogo, kto zareagowałby przez 2 lata w wątek, którego interesujący temat trzeba czytać stosunkowo często.
źródło
Edycja : (po zmianie kodu)
Nie możemy powiedzieć Ci, czy chcesz zadzwonić do rodzica
__init__
(lub innej funkcji). Dziedziczenie oczywiście działałoby bez takiego wezwania. Wszystko zależy od logiki twojego kodu: na przykład, jeśli wszystko__init__
robisz w klasie nadrzędnej, możesz po prostu__init__
całkowicie pominąć klasę podrzędną .rozważ następujący przykład:
>>> class A: def __init__(self, val): self.a = val >>> class B(A): pass >>> class C(A): def __init__(self, val): A.__init__(self, val) self.a += val >>> A(4).a 4 >>> B(5).a 5 >>> C(6).a 12
źródło
Nie ma twardej i szybkiej reguły. Dokumentacja klasy powinna wskazywać, czy podklasy powinny wywoływać metodę nadklasy. Czasami chcesz całkowicie zastąpić zachowanie superklasy, a innym razem je rozszerzyć - tj. Wywołać swój własny kod przed i / lub po wywołaniu nadklasy.
Aktualizacja: ta sama podstawowa logika ma zastosowanie do każdego wywołania metody. Konstruktory czasami wymagają szczególnej uwagi (ponieważ często ustawiają stan, który określa zachowanie), a destruktory, ponieważ są konstruktorami równoległymi (np. Przy alokacji zasobów, np. Połączeń z bazami danych). Ale to samo może dotyczyć, powiedzmy,
render()
metody widgetu.Dalsza aktualizacja: co to jest OPP? Masz na myśli OOP? Nie - podklasa często musi wiedzieć coś o projekcie nadklasy. Nie wewnętrzne szczegóły implementacji - ale podstawowa umowa, którą nadklasa ma ze swoimi klientami (przy użyciu klas). Nie narusza to w żaden sposób zasad OOP. Dlatego
protected
jest to ogólnie poprawne pojęcie w OOP (choć oczywiście nie w Pythonie).źródło
IMO, powinieneś to nazwać. Jeśli twoja superklasa jest
object
, nie powinieneś, ale w innych przypadkach myślę, że wyjątkowe jest nie nazywać tego. Jak już odpowiedzieli inni, jest to bardzo wygodne, jeśli twoja klasa nie musi nawet nadpisywać__init__
samej siebie, na przykład gdy nie ma (dodatkowego) stanu wewnętrznego do zainicjowania.źródło
Tak, zawsze powinieneś
__init__
jawnie wywoływać klasę bazową jako dobrą praktykę kodowania. Zapomnienie o tym może spowodować subtelne problemy lub błędy w czasie wykonywania. Dzieje się tak, nawet jeśli__init__
nie przyjmuje żadnych parametrów. Jest to odmienne od innych języków, w których kompilator niejawnie wywoła za Ciebie konstruktor klasy bazowej. Python tego nie robi!Głównym powodem zawsze wywoływania klasy bazowej
_init__
jest to, że klasa bazowa może zazwyczaj tworzyć zmienne składowe i inicjować je do wartości domyślnych. Więc jeśli nie wywołasz init klasy bazowej, żaden z tego kodu nie zostanie wykonany i otrzymasz klasę bazową, która nie ma zmiennych składowych.Przykład :
class Base: def __init__(self): print('base init') class Derived1(Base): def __init__(self): print('derived1 init') class Derived2(Base): def __init__(self): super(Derived2, self).__init__() print('derived2 init') print('Creating Derived1...') d1 = Derived1() print('Creating Derived2...') d2 = Derived2()
To drukuje ...
Uruchom ten kod .
źródło