2015-12-21 3 views
8

Ho due oggetti definiti dall'utente, ad esempio a e b.
Entrambi questi oggetti hanno gli stessi valori hash.
Tuttavia, lo id(a) e lo id(b) non sono uguali.Differenza tra hash() e id()

Inoltre,

>>> a is b 
False 
>>> a == b 
True 

Da questa osservazione, posso dedurre quanto segue?

  • Gli oggetti non uguali possono avere gli stessi valori hash.
  • Gli oggetti uguali devono avere gli stessi valori id.
  • Ogniqualvolta si chiama obj1 is obj2, vengono confrontati i valori id di entrambi gli oggetti, non i loro valori hash.
+0

La tua seconda inferenza sarebbe facilmente invalidata confrontando 'id (a)' e 'id (b)'. – chepner

+0

@chepner Grazie. Ho capito ora –

+0

Ho scritto come vengono calcolati gli hash per i tipi built-in [qui] (http://delimitry.blogspot.ru/2014/07/python-hash-calculation-algorithms.html). Si può notare che il calcolo di 'hash' per tipo di oggetto dipende dal suo' id'. – Delimitry

risposta

17

Ci sono tre concetti da afferrare quando si cerca di capire id, hash e gli operatori == e is: identità, valore valore e hash. Non tutti gli oggetti hanno tutti e tre.

  1. Tutti gli oggetti hanno un un'identità, sebbene anche questo può essere un po 'scivoloso in alcuni casi. La funzione id restituisce un numero corrispondente all'identità di un oggetto (in cpython restituisce l'indirizzo di memoria dell'oggetto, ma altri interpreti possono restituire qualcos'altro). Se due oggetti (che esistono allo stesso tempo) hanno la stessa identità, in realtà sono due riferimenti allo stesso oggetto. L'operatore is confronta gli elementi per identità, a is b equivale a id(a) == id(b).

    L'identità può creare un po 'di confusione quando si gestiscono oggetti che sono memorizzati nella cache da qualche parte nella loro implementazione. Ad esempio, gli oggetti per i piccoli numeri interi e le stringhe in cpython non vengono rifatti ogni volta che vengono utilizzati. Invece, gli oggetti esistenti vengono restituiti ogni volta che sono necessari. Non dovresti comunque fare affidamento su questo nel tuo codice, perché è un dettaglio di implementazione di cpython (altri interpreti potrebbero farlo in modo diverso o non farlo affatto).

  2. Tutti gli oggetti hanno anche un valore , anche se questo è un po 'più complicato. Alcuni oggetti non hanno un valore significativo diverso dalla loro identità (quindi, in alcuni casi, anche l'identità può essere un valore). Il valore può essere definito come quello che l'operatore == confronta, quindi in qualsiasi momento a == b, è possibile affermare che lo a e lo hanno lo stesso valore. Gli oggetti contenitore (come gli elenchi) hanno un valore definito dal loro contenuto, mentre altri tipi di oggetti avranno valori basati sui loro attributi. Oggetti di tipi diversi possono a volte avere gli stessi valori, come con i numeri: 0 == 0.0 == 0j == decimal.Decimal("0") == fractions.Fraction(0) == False (sì, bool s sono numeri in Python, per ragioni storiche).

    Se una classe non definisce un metodo __eq__ (per implementare l'operatore ==), esso erediterà la versione di default da object e le sue istanze saranno confrontati esclusivamente dalla loro identità. Questo è appropriato quando le istanze altrimenti identiche possono avere importanti differenze semantiche. Ad esempio, due socket diversi connessi alla stessa porta dello stesso host devono essere trattati in modo diverso se uno sta recuperando una pagina Web HTML e l'altra sta ottenendo un'immagine collegata da quella pagina, quindi non hanno lo stesso valore.

  3. Oltre a un valore, alcuni oggetti hanno un valore hash , che significa che possono essere utilizzati come chiavi di dizionario (e memorizzati in set s). La funzione hash(a) restituisce il valore hash dell'oggetto a, un numero basato sul valore dell'oggetto. L'hash di un oggetto deve rimanere uguale per tutta la vita dell'oggetto, quindi ha senso che un oggetto sia lavabile se il suo valore è immutabile (perché è basato sull'identità dell'oggetto o perché è basato sul contenuto del oggetto che sono a loro volta immutabili).

    Più oggetti diversi possono avere lo stesso valore di hash, anche se le funzioni di hash ben progettate eviteranno il più possibile. Memorizzare oggetti con lo stesso hash in un dizionario è molto meno efficiente rispetto all'archiviazione di oggetti con hash distinti (ogni collisione hash richiede più lavoro). Gli oggetti sono lavabili per impostazione predefinita (poiché il loro valore predefinito è la loro identità, che è immutabile). Se scrivi un metodo __eq__ in una classe personalizzata, Python disabiliterà questa implementazione di hash predefinita, poiché la tua funzione __eq__ definirà un nuovo significato di valore per le sue istanze. Dovrai scrivere anche un metodo __hash__, se vuoi che la tua classe sia ancora lavabile. Se si eredita da una classe hashable ma non si desidera essere autonomamente lavabili, è possibile impostare __hash__ = None nel corpo della classe.

+1

grande spiegazione, grazie –

+0

Spiegazione molto approfondita, e mi piace il suggerimento sull'implementazione del proprio metodo __hash__ quando personalizzi __eq__ per le tue classi. Non lo sapevo! – AvlWx

+0

Ottima risposta! Una domanda: il valore di hash viene usato come chiave del dizionario per quale dizionario? Un dizionario di cosa, un dizionario di localizzazione della memoria? – bretonics

1

La funzione di hash viene utilizzato per:

confrontare rapidamente le chiavi del dizionario nel corso di una consultazione dei dizionari

la funzione ID viene utilizzato per:

ritorno “l'identità "Di un oggetto. Questo è un numero intero che è garantito essere unico e costante per questo oggetto durante la sua vita. Due oggetti con vite non sovrapposte possono avere lo stesso valore id().

+0

Ho fatto questa lettura prima di postare la domanda qui. –

5

Gli oggetti non uguali possono avere gli stessi valori di hash.

Sì, questo è vero. Un semplice esempio è hash(-1) == hash(-2) in CPython.

Gli oggetti uguali devono avere gli stessi valori ID.

No questo è falso in generale. Un semplice controesempio annotato da @chepner è quello 5 == 5.0 ma id(5) != id(5.0).

Ogni volta che viene chiamato, obj1 is obj2, vengono confrontati i valori ID di entrambi gli oggetti, non i loro valori hash.

Sì, questo è vero. is confronta lo id degli oggetti per l'uguaglianza (in CPython è l'indirizzo di memoria dell'oggetto). Generalmente, questo non ha nulla a che fare con il valore hash dell'oggetto (l'oggetto non deve nemmeno essere lavabile).

+2

Un controesempio per la seconda asserzione: '5 == 5.0' contro' id (5) == id (5.0) '. – chepner

+1

@ajcr Anche '[] is []' restituisce 'False' anche se' id ([]) == id ([]) 'restituisce' True'. Puoi spiegare anche questo? –

+0

@chepner: è un controesempio più bello. Ti dispiace se lo rubo per la risposta (con attribuzione, ovviamente)? –