Due oggetti non sono garantiti per l'hash allo stesso valore a meno che non siano uguali [1].
funzioni Python (compresi lambda) non risultano uguali, anche se hanno codice identico [2]. Ad esempio:
>>> (lambda: 1) == (lambda: 1)
False
Attuazione-saggio, questo comportamento è dovuto al fatto che gli oggetti funzionali non forniscono il proprio operatore di uguaglianza. Invece, ereditano quello predefinito che utilizza l'identità dell'oggetto, cioè il suo indirizzo. Dal documentation:
Se non è definita alcuna __cmp__()
, __eq__()
o __ne__()
operazione, classe istanze vengono confrontati per identità di oggetto (“indirizzo”).
Ecco ciò che accade nel vostro esempio particolare:
fn = lambda: 1 # New function is allocated at address A and stored in fn.
fn = lambda: 1 # New function is allocated at address B and stored in fn.
# The function at address A is garbage collected.
fn = lambda: 1 # New function is allocated at address A and stored in fn.
# The function at address B is garbage collected.
fn = lambda: 1 # New function is allocated at address B and stored in fn.
# The function at address A is garbage collected.
...
Dato indirizzo A
è sempre hash per un valore, e l'indirizzo B
ad un altro, si sta vedendo hash(fn)
si alternano tra i due valori. Questo comportamento alternato è, tuttavia, un artefatto di implementazione e potrebbe cambiare un giorno se, ad esempio, il garbage collector si comportasse in modo leggermente diverso.
La seguente nota penetranti è stato contributi di @ruakh:
Vale la pena notare che non è possibile scrivere un processo generale per determinare se due funzioni sono equivalenti. (Questa è una conseguenza della undecidability del halting problem.) Inoltre, due funzioni Python possono comportano in modo diverso, anche se il loro codice è identico (poiché possono essere riferiti a chiusure variabili distinte-but-nomi identici). Quindi è logico che le funzioni di Python di non sovraccarichino l'operatore di uguaglianza: non c'è modo di implementare qualcosa di meglio del confronto di identità di oggetto predefinito .
[1] L'inverso non è generalmente vero: due oggetti che si confrontano in modo non uguale possono avere lo stesso valore di hash. Questo è chiamato hash collision.
[2] Calling tuoi lambda e quindi hashing il risultato sarebbe ovviamente sempre dare lo stesso valore in quanto hash(1)
è sempre lo stesso all'interno di un programma:
>>> (lambda: 1)() == (lambda: 1)()
True
Giusto per essere chiari, ci si rende conto che si sta tagliando l'oggetto della funzione stessa, non si sta tagliando il valore che restituirebbe quando chiamato, giusto? –
Probabilmente è una cattiva idea, ma potresti semplicemente considerare l'oggetto codice: 'hash ((lambda: 1) .__ code __)' – berdario
solo curioso ... a cosa servono i modelli per cui avresti bisogno di questa funzionalità? frequenti le cose nei dizionari in base a una funzione arbitraria? –