2009-08-24 7 views
9

Czy istnieje sposób na wygenerowanie identyfikatora hash-like dla obiektów w pythonie, który jest oparty wyłącznie na wartościach atrybutów obiektów? Na przykład:Generowanie unikalnego identyfikatora obiektu Pythona na podstawie jego atrybutów

class test: 
    def __init__(self, name): 
     self.name = name 

obj1 = test('a') 
obj2 = test('a') 

hash1 = magicHash(obj1) 
hash2 = magicHash(obj2) 

Czego szukam to coś, w czym hash1 == hash2. Czy coś takiego istnieje w python? Wiem, że mogę przetestować, czy jest to obj1.name == obj2.name, ale szukam czegoś ogólnego, którego mogę użyć na dowolnym obiekcie.

Odpowiedz

6

Masz na myśli coś takiego? Używając specjalnej metody __hash__

class test: 
    def __init__(self, name): 
     self.name = name 
    def __hash__(self): 
     return hash(self.name) 

>>> hash(test(10)) == hash(test(20)) 
False 
>>> hash(test(10)) == hash(test(10)) 
True 
+2

Nie można zagwarantować, że będzie wyjątkowy. –

+0

@Bastien, masz rację. Ale to naprawdę zależy od aplikacji. Dla wielu przypadków hash może wystarczyć. –

+1

Nie zaleca się zwracania czegokolwiek z __hash __ (self) oprócz int (http://docs.python.org/reference/datamodel.html#object.__hash__), ponieważ spowoduje to renderowanie obiektu z pozoru ale niepoprawnie hashable (jak w używane w dyktach) – SingleNegationElimination

3

Aby uzyskać niepowtarzalny porównania:

być unikalne można szeregować dane, a następnie porównać wartość zserializowaną aby upewnić się, że pasuje dokładnie.

Przykład:

import pickle 

class C: 
    i = 1 
    j = 2 

c1 = C() 
c2 = C() 
c3 = C() 
c1.i = 99 

unique_hash1 = pickle.dumps(c1) 
unique_hash2 = pickle.dumps(c2) 
unique_hash3 = pickle.dumps(c3) 

unique_hash1 == unique_hash2 #False 
unique_hash2 == unique_hash3 #True 

Jeśli nie potrzebują unikalne wartości dla każdego obiektu, ale przede wszystkim wyjątkowy:

Uwaga ta sama wartość będzie zawsze sprowadzić do tego samego hash, ale 2 różne wartości mogły zmniejszyć się do tego samego skrótu.

Nie można używać czegoś takiego funkcji wbudowanej hash() (o ile nie zastępują __hash__)

hash(c1) == hash(c2) #False 
hash(c2) == hash(c3) #False <--- Wrong 

lub coś podobnego serializacji danych przy użyciu marynowane, a następnie użyć zlib.crc32.

import zlib 
crc1 = zlib.crc32(pickle.dumps(c1)) 
crc2 = zlib.crc32(pickle.dumps(c2)) 
crc3 = zlib.crc32(pickle.dumps(c3)) 
crc1 == crc2 #False 
crc2 == crc3 #True 
+0

Dla unikalnego porównania możesz również użyć zlib.compress, aby uczynić reprezentację trochę mniejszą jeśli twoje obiekty są bardzo duże. –

+0

Nie, marynata nie nadaje się do mieszania. Wyniki mogą się różnić, jak opisał Robert Brewer: http://www.aminus.org/blogs/index.php/2007/11/03/pickle_dumps_not_suitable_for_hashing?blog=2 –

+0

Nie wiem, dlaczego, ale z CPython 2.5.1 I nie może odtworzyć swojego zachowania. Zawsze ma dla mnie ten sam wynik. –

2

Chyba

def hash_attr(ins): 
return hash(tuple(ins.__dict__.items())) 

skróty wystąpienie coś na podstawie jego atrybutów.

+1

Dopóki wszystkie atrybuty są nieosiągalne .. –