2010-09-02 11 views
39

Załóżmy, że mam namedtuple takiego:Jak zapewnić dodatkową inicjalizację dla podklasy namedtuple?

EdgeBase = namedtuple("EdgeBase", "left, right") 

Chcę zaimplementować niestandardową hash-funkcja dla tego, więc tworzę następujące podklasy:

class Edge(EdgeBase): 
    def __hash__(self): 
     return hash(self.left) * hash(self.right) 

Ponieważ obiekt jest niezmienny, ja chcą hash-wartość oblicza się tylko raz, więc mogę to zrobić:

class Edge(EdgeBase): 
    def __init__(self, left, right): 
     self._hash = hash(self.left) * hash(self.right) 

    def __hash__(self): 
     return self._hash 

to wydaje się działać, ale nie jestem pewien, o s ubclassing i inicjalizacja w Pythonie, szczególnie z krotkami. Czy są jakieś pułapki na to rozwiązanie? Czy istnieje zalecany sposób, aby to zrobić? Czy to w porządku? Z góry dziękuję.

+1

Testy wykazały, że nie warto buforować wartości hash, zobacz [issue # 9685] (https://mail.python.org/pipermail/python-bugs-list/2013- January/192363.html) –

Odpowiedz

44

edycja na rok 2017:turns out namedtuple isn't a great idea. attrs to nowoczesna alternatywa.

class Edge(EdgeBase): 
    def __new__(cls, left, right): 
     self = super(Edge, cls).__new__(cls, left, right) 
     self._hash = hash(self.left) * hash(self.right) 
     return self 

    def __hash__(self): 
     return self._hash 

__new__ to, co chcesz zadzwonić tutaj bo krotki są niezmienne. Niezmienne obiekty są tworzone w __new__, a następnie zwracane do użytkownika, zamiast zapełniania danymi w __init__.

cls musi być przekazywane dwa razy na wezwanie super na __new__ ponieważ __new__ jest na historycznych/dziwnych powodów niejawnie w staticmethod.

+3

Dlaczego powinienem preferować to do mojego rozwiązania (to nie jest cyniczne pytanie, naprawdę chcę zrozumieć, czy są jakieś znaczące różnice)? –

+9

Twoje rozwiązanie nie używa 'super', a zatem może przerwać każdy rodzaj dziedziczenia wielokrotnego. Nie ma znaczenia, czy * ty * nie używasz MI; jeśli zrobi to ktoś inny, ich kod złamie się okropnie. Nie jest trudno uniknąć tego problemu, używając wszędzie "super". – habnabit

+5

Korzystne jest również dodanie do klasy '__slots__ =()'. W przeciwnym razie zostanie utworzone '__dict__', które neguje efektywność pamięci' namedtuple'. – WGH

3

Kod w pytaniu może skorzystać z połączenia super w __init__, jeśli kiedykolwiek zostanie zdyskwalifikowany w sytuacji wielokrotnego dziedziczenia, ale poza tym jest poprawny.

class Edge(EdgeBase): 
    def __init__(self, left, right): 
     super(Edge, self).__init__(a, b) 
     self._hash = hash(self.left) * hash(self.right) 

    def __hash__(self): 
     return self._hash 

Podczas krotki są tylko do odczytu tylko części krotką ich podklasy są tylko do odczytu, inne właściwości mogą być napisane jak zwykle co jest, co pozwala na przypisanie do _hash niezależnie od tego, czy jest to zrobione w __init__ lub __new__. Możesz uczynić podklasę w pełni readonly, ustawiając ją na __slots__ na(), co ma dodatkową zaletę polegającą na oszczędzaniu pamięci, ale wtedy nie będziesz mógł jej przypisać do _hash.

Powiązane problemy