2012-11-21 10 views
6

Próbuję utworzyć niestandardową funkcję haszującą dla ciągów. Chcę ciągi skrótów według ich częstotliwości znaków na wagę. Aby hi i ih przyniosły ten sam skrót. Czy mogę przesłonić __hash__?Python Overriding String __hash__

Lub tworzy klasę opakowania, która przechowuje ciąg i przesłonięcie __hash__ i __eq__ tylko w jedną stronę?

+0

Używam klasy opakowania. Jest to miłe i wyraźne i nie spowoduje zamieszania, udając sznur, a jednocześnie całkowicie odmiennie. – millimoose

+0

@millimoose good point – darksky

+1

Mieszanie 'str' i' custom_str' w kontenerze używającym skrótu jako kluczy może być interesujące :) –

Odpowiedz

4

Potrzebujesz typu pochodnego z inną semantyką równości. Zwykle podejście będzie polegało na zdefiniowaniu sposobu działania równości, a następnie na skonstruowaniu metody haszowania ze struktur tam wyprowadzonych, ponieważ konieczne jest, aby hasz przystał na równość. To może być:

import collections 

class FrequencyString(str): 
    @property 
    def normalized(self): 
     try: 
      return self._normalized 
     except AttributeError: 
      self._normalized = normalized = ''.join(sorted(collections.Counter(self).elements())) 
      return normalized 

    def __eq__(self, other): 
     return self.normalized == other.normalized 

    def __hash__(self): 
     return hash(self.normalized) 
+1

Załóżmy, że tworzę wolną funkcję, która zwróci skrót. Jak wstawiłbym ten klucz na zwróconej pozycji hash? Czy 'dict = {}, dict [5] = wartość' wstaw" wartość "w pozycji 5, czy klucz" 5 "? – darksky

+0

umieszczanie wartości w dyktach * nie * implikuje zmianę semantyki równości, to jest właściwa droga do tego. Alternatywnie można utworzyć strukturę obwiedni jako obwiedni, która ma oryginalny ciąg jako atrybut instancji. – SingleNegationElimination

0

Twoje założenie jest prawidłowe, nie można przesłonić klauzul podstawowych w Pythonie. Chociaż może oczywiście zastąpić to, co zrobi str(), nie będzie działać dla literałów łańcuchowych.

Jeśli piszesz kod pre-python 2.2 spojrzenie na klasy UserString jeśli chcesz stworzyć swój własny: http://docs.python.org/2/library/userdict.html#module-UserString

W przeciwnym razie można po prostu odziedziczyć str lub unicode

w twoim przypadku po prostu nadpisuje __hash__ metoda jest wystarczająca, jeśli chcesz użyć jej jako klucza dyktującego. Ale jeśli szukasz miejsca na porównaniach niż trzeba by zastąpić __eq__ lub __cmp__

+0

Argghghh - nie, nie i nie dla "UserString" - to jest historia starożytna - dziedziczy po prostu 'str', na przykład' class mystr (str): ... ' –

+0

Mówi, że jeśli nie potrzebuję obsługi wstecznej kompatybilności dla wersji przed 2.2, mogę po prostu podklasować bezpośrednio z wbudowanego w 'str'. Jak to się stało? Czy to po prostu: 'class wrapper_class (str):'? Czy tylko przesłonięcie '__hash__' i' __eq__' byłoby wystarczające? – darksky

+0

@JonClements właśnie odpowiedziałeś na część 1 mojego komentarza :) A co z metodami zastępowania? Czy '__hash__' i' __eq__' będą wystarczające? – darksky

0

można dziedziczyć str, ale ponieważ te są niezmienne trzeba podklasy je w nieco inny sposób. Najprawdopodobniej będziesz chciał utworzyć nowe z istniejących ciągów, więc musisz także zastąpić metodę __new__. Możesz także zastosować dodatkowe specjalne metody, aby pokonać optymalizacje Pythona.

Oto przykład podklasy wbudowanego obiektu str, obiektu mapstr, który umożliwia łatwe zastępowanie symboli zastępczych w formularzach.

+0

Czy możesz wyjaśnić, co masz na myśli przez "Najprawdopodobniej będziesz chciał tworzyć nowe z istniejących ciągów"? Po prostu chcę utworzyć jeden ciąg znaków, który mogę przekazać do '__init__', więc dlaczego miałbym potrzebować podklasy' __new__'? Co się stanie, jeśli nie będę? – darksky

+0

Więc jak zamierzasz z niego korzystać? – Keith

+0

Szarpanie ciągów, które mają te same znaki w jednym kluczu. Wartością jest lista wszystkich ciągów. Klucz to posortowana ich wersja. – darksky