2010-01-09 13 views
29

Oto dwojakie pytanie, z części teoretycznej i praktycznej:Dyktowanie podklasowe: powinno się nazywać .__ init __()?

Kiedy rozszerzasz DICT:

class ImageDB(dict): 
    def __init__(self, directory): 
     dict.__init__(self) # Necessary?? 
     ... 

powinien dict.__init__(self) nazwać, po prostu jako „bezpieczeństwa” środka (na przykład w przypadku, są pewne nietrywialne szczegóły dotyczące implementacji, które mają znaczenie)? czy istnieje ryzyko, że kod zerwie z przyszłą wersją Pythona, jeśli zostanie wywołana dict.__init__(), a nie , a nie? Szukam podstawowego powodu robienia jednej rzeczy lub drugiej, tutaj (praktycznie, wywołanie dict.__init__() jest bezpieczne).

Domyślam się, że kiedy jest wywoływane ImageDB.__init__(self, directory), self jest już nowym pustym obiektem dict, a zatem nie ma potrzeby wywoływania dict.__init__ (chcę, aby dyktatura była pusta, na początku). Czy to jest poprawne?

Edit:

Im bardziej praktyczne pytanie za fundamentalne pytanie powyższego jest następujący. Myślałem o podklasowaniu dict, ponieważ często używałbym składni db [...] (zamiast robić db.contents [...] cały czas); jedyne dane obiektu (atrybut) jest rzeczywiście naprawdę dyktować. Chcę dodać kilka metod do bazy danych (na przykład get_image_by_name(), lub get_image_by_code(), na przykład) i tylko przesłonić __init__(), ponieważ baza danych obrazu jest zdefiniowany przez katalog, który go zawiera.

Podsumowując, The (praktyczne) pytanie mogłoby być: co jest dobre, realizacja czegoś, co zachowuje się jak słownik, chyba że jej inicjalizacji jest inny (to zajmie tylko nazwę katalogu), oraz że posiada dodatkowy metody?

"Fakty" zostały wymienione w wielu odpowiedziach. Więc domyślam się, że wszystko sprowadza się do: czy podklasujesz dict, zastępujesz __init__() i dodajesz metody, czy też piszesz funkcję (fabryczną), która zwraca dict, do którego dodajesz metody? Jestem skłonny preferować pierwsze rozwiązanie, ponieważ funkcja fabryka zwraca obiekt, którego typ nie wskazuje, że ma on dodatkową semantykę i metody, ale co o tym myślisz?

Edycja 2:

wnoszę z odpowiedzią każdego człowieka, że ​​nie jest to dobry pomysł, aby podklasy dict gdy nowa klasa „nie jest słownik”, aw szczególności, gdy jego metoda __init__ nie może samo argumenty jako dict's __init__ (co ma miejsce w "pytaniu praktycznym" powyżej). Innymi słowy, jeśli dobrze rozumiem, konsensus wydaje się być następujący: podczas podklasy wszystkie metody (w tym inicjalizacja) muszą mieć ten sam podpis co metody klasy bazowej. Dzięki temu isinstance (subclass_instance, dict) gwarantuje, że można na przykład użyć subclass_instance.__init__(), np. dict.__init__().

Pojawia się kolejne praktyczne pytanie: w jaki sposób powinna zostać wdrożona klasa, która jest podobna do metody dyktowania, z wyjątkiem metody inicjowania? bez podklasy? to wymagałoby jakiegoś uciążliwego kodu, nie?

+0

Funkcja fabryczna jest drogą do zrobienia. Jeśli chcesz dostosować zachowanie * instancji *, możesz utworzyć podklasę. Jeśli chcesz tylko nadpisać * inicjalizację * nie musisz niczego podklasować, ponieważ twoje instancje nie różnią się od standardowych. Pamiętaj, że __init__ nie jest częścią interfejsu instancji, ale klasy. –

+0

O ile widzę to dla tego problemu byłoby najlepiej dodać metodę "__getitem__" do ImageDB zamiast podklasy dyktować, ponieważ to _nie_ dykt. To pozwala ci robić to, co chcesz, bez użycia wszystkich takich metod jak 'pop()', które wydają się nieodpowiednie dla twojej klasy. –

+0

@gs: Dobra uwaga, o pop; rzeczywiście jest to, przynajmniej na razie, nieistotne (zawartość bazy danych jest zdefiniowana tylko podczas inicjalizacji). Uważam, że najlepiej jest, jeśli implementacja ściśle pasuje do niezbędnych funkcji. – EOL

Odpowiedz

15

Powinieneś prawdopodobnie wywołać dict.__init__(self) podczas podklasowania; w rzeczywistości nie wiesz, co dokładnie dzieje się w dyktowaniu (ponieważ jest to wbudowane) i może się różnić w różnych wersjach i implementacjach. Nie wywołanie go może skutkować niewłaściwym zachowaniem, ponieważ nie możesz wiedzieć, gdzie dict trzyma swoje wewnętrzne struktury danych.

Przy okazji, nie powiedziałeś nam, co chcesz zrobić, aby wykonać; jeśli chcesz zachować zachowanie klasy z dyktowaniem (odwzorowywaniem) i naprawdę nie potrzebujesz dyktatu (np. nie ma kodu wykonującego isinstance(x, dict) w dowolnym miejscu w oprogramowaniu, tak jak powinno być), prawdopodobnie lepiej wykorzystasz UserDict.UserDict lub UserDict.DictMixin jeśli używasz python < = 2.5 lub collections.MutableMapping, jeśli używasz Pythona> = 2.6. Zapewni to klasie doskonałe zachowanie dyktatorskie.

EDYCJA: Czytałem w innym komentarzu, że nie przeważasz żadnej metody dicta! W ogóle nie ma sensu podklasy, nie rób tego.

def createImageDb(directory): 
    d = {} 
    # do something to fill in the dict 
    return d 

EDYCJA 2: chcesz dziedziczyć po dict, aby dodać nowe metody, ale nie musisz nadpisywać żadnych. Niż dobrym wyborem może być:

class MyContainer(dict): 
    def newmethod1(self, args): 
     pass 

    def newmethod2(self, args2): 
     pass 


def createImageDb(directory): 
    d = MyContainer() 
    # fill the container 
    return d 

Przy okazji: jakie metody dodajecie? Czy na pewno tworzysz dobrą abstrakcję? Może lepiej skorzystaj z klasy, która definiuje potrzebne ci metody i używaj do niej "normalnego" dyktatu.

Fabryka func: http://en.wikipedia.org/wiki/Factory_method_pattern

To po prostu sposób powierzania budowy instancji do funkcji zamiast przesłanianie/zmieniając jego konstruktorów.

+2

+1: Podklasowanie, gdy nie ma potrzeby tworzenia podklasy, jest złym pomysłem, fabryka jest znacznie lepsza. –

+0

Nawet jeśli nie zastąpię metod dyktowania, nowa klasa ma dodatkowe metody, ... (Badam fabryki, dziękuję za wskaźnik!) – EOL

+0

Nie jestem pewien co do UserDict: w dokumentacji czytamy: "Ten moduł również definiuje klasę UserDict, która działa jako wrapper wokół obiektów słownika, a zapotrzebowanie na tę klasę zostało w dużej mierze zastąpione przez zdolność do podklasy bezpośrednio z dict (funkcja dostępna od wersji Python 2.2). – EOL

2

PEP 372 dotyczy dodania zamówionego dyktowania do modułu kolekcji.

Ostrzega, że ​​"podklasy dyktowania nie są trywialne, a wiele implementacji nie zastępuje właściwie wszystkich metod, co może prowadzić do nieoczekiwanych rezultatów."

Proponowany (i zaakceptowane) patch do python3.1 wykorzystuje __init__ który wygląda następująco:

+class OrderedDict(dict, MutableMapping): 
+ def __init__(self, *args, **kwds): 
+  if len(args) > 1: 
+   raise TypeError('expected at most 1 arguments, got %d' % len(args)) 
+  if not hasattr(self, '_keys'): 
+   self._keys = [] 
+  self.update(*args, **kwds) 

Na tej podstawie, wygląda dict.__init__() nie musi być nazywany

. Edycja: Jeśli nie zastępujesz ani nie rozszerzasz żadnej z metod dict, to zgadzam się z Alanem Franzoni: używaj raczej fabryki dict niż podklasy:

def makeImageDB(*args,**kwargs): 
    d = {} 
    # modify d 
    return d 
+0

To jest interesujące. Teraz, nie wywoływanie 'dict .__ init __()' w Pythonie 3.1 jest bezpieczne, ale co z przyszłością? Ponieważ nie zastępuję żadnej metody, w ImageDB podklasowanie jest bardzo bezpieczne; tylko inicjalizacja jest wyjątkowa (buduje dyktando). – EOL

+0

Przepraszam, EOL, nie podążam za tobą. Moim zdaniem Python 3.1 jest przyszłością ... :) – unutbu

+0

Weź pod uwagę, co faktycznie robi init. Aktualizuje dyktor z wszystkimi argumentami i słowami kluczowymi. To jest coś, co twoja klasa będzie musiała zrobić, więc wywołanie dyktatu. \ _ \ _ Init __ (self, * args, ** kwds) prawdopodobnie zaopiekuje się tym dla ciebie, albo będziesz musiał zadzwonić self.update, jak OrderedDict robi. –

10

Powinieneś ogólnie nazwać klasę podstawową "__init__, dlaczego więc zrobić tutaj wyjątek?

albo nie zastępują __init__ albo jeśli trzeba zastąpić __init__ rozmowę klasę bazową __init__, Jeśli martwisz się o argumenty po prostu przekazać * args, ** kwargs albo nic, jeśli chcesz na przykład pusty dict

class MyDict(dict): 
    def __init__(self, *args, **kwargs): 
     myparam = kwargs.pop('myparam', '') 
     dict.__init__(self, *args, **kwargs) 

Nie należy zakładać, co robi klasy bazowej lub nie robi, że jest źle, aby nie wywołać klasa bazowa __init__

+0

Wywoływanie dykcji '__init__' jest w rzeczywistości Obecnie robię. Ponieważ wygląda na to, że wywoływanie go bez argumentów nic nie robi, jestem po prostu ciekawy podstawowych faktów na temat Pythona, które pozwoliłyby na to, aby go nie wywoływać! – EOL

+0

@EOL, IMO jest po prostu nie w porządku, aby nie wywoływać baseclass __init__, dopóki nie ma bardzo silnego powodu, aby zrobić inaczej –

+0

@Anurag: Rozumiem twój punkt widzenia. Próbuję nieco pogłębić moją wiedzę na temat Pythona i zastanawiałem się, czy taki "bardzo bardzo silny powód" dla braku wywoływania 'dykta .__ init __ (self)' (bez żadnych innych argumentów) istnieje (jak "to będzie nigdy nic nie rób "). – EOL

3

Strzeż wytrawiania gdy instacji dict; to na przykład wymaga __getnewargs__ w 2.7, i może __getstate__ __setstate__ w starszych wersjach. (Nie mam pojęcia dlaczego.)

class Dotdict(dict): 
    """ d.key == d["key"] """ 

    def __init__(self, *args, **kwargs): 
     dict.__init__(self, *args, **kwargs) 
     self.__dict__ = self 

    def __getnewargs__(self): # for cPickle.dump(d, file, protocol=-1) 
     return tuple(self) 
Powiązane problemy