2014-09-30 11 views
38

mi aspettavo le seguenti due tuplePerché le tuple costruite da insiemi diversamente inizializzati sono uguali?

>>> x = tuple(set([1, "a", "b", "c", "z", "f"])) 
>>> y = tuple(set(["a", "b", "c", "z", "f", 1])) 

per confrontare diseguale, ma non lo fanno:

>>> x == y 
>>> True 

Perché?

+22

perché ti aspetti che sia False - il contenuto dei due set è lo stesso! –

+0

[Relevant.] (Http://stackoverflow.com/a/26099671/1763356) Come dice Zero Pireo, tuttavia, la randomizzazione dell'hash rende questa falsità ** per stringhe, byte e date ** come 'hash (a_string)' cambia ogni volta che installi l'interprete. – Veedrac

risposta

67

A prima vista, sembra che x voglia sempre uguale y, perché due insiemi costruiti dagli stessi elementi sono sempre uguali:

>>> x = set([1, "a", "b", "c", "z", "f"]) 
>>> y = set(["a", "b", "c", "z", "f", 1]) 
>>> x 
{1, 'z', 'a', 'b', 'c', 'f'} 
>>> y 
{1, 'z', 'a', 'b', 'c', 'f'} 
>>> x == y 
True 

Tuttavia, è non sempre il caso che tuple (o altre raccolte ordinate) costruite da due serie uguali sono uguali.

In effetti, il risultato del confronto è talvolta True e talvolta False, almeno in Python> = 3.3. Testare il codice seguente:

# compare.py 
x = tuple(set([1, "a", "b", "c", "z", "f"])) 
y = tuple(set(["a", "b", "c", "z", "f", 1])) 
print(x == y) 

... mille volte:

$ for x in {1..1000} 
> do 
> python3.3 compare.py 
> done | sort | uniq -c 
147 False 
853 True 

Questo perché, dal momento che Python 3.3, i valori di hash di stringhe, byte e datetimes sono randomizzati a seguito di un security fix. A seconda di cosa sono gli hash, possono verificarsi "collisioni", il che significa che gli articoli dell'ordine sono memorizzati nell'array sottostante (e quindi l'ordine di iterazione) dipende dall'ordine di inserimento.

Ecco il bit rilevante dalla documentazione:

miglioramenti di sicurezza:

  • Hash randomizzazione è attivata per impostazione predefinita.

- https://docs.python.org/3/whatsnew/3.3.html

EDIT: Dal momento che è menzionato nei commenti che il rapporto True/False sopra è superficialmente sorprendente ...

insiemi, come dizionari, vengono implementate come hash tabelle - quindi se c'è una collisione, l'ordine degli elementi nella tabella (e quindi l'ordine di iterazione) dipenderà sia dall'elemento che è stato aggiunto per primo (diverso in x e in t il suo caso) e il seme utilizzato per l'hashing (diverso per le invocazioni di Python dal 3.3). Dato che le collisioni sono rare per progettazione e gli esempi in questa domanda sono insiemi piccoli, il problema non si pone tutte le volte che si potrebbe supporre inizialmente.

Per una spiegazione completa dell'implementazione di Python di dizionari e insiemi, vedere The Mighty Dictionary.

+0

Provato con Python 2.7.3, 'per x in {1..5000}', l'output è '5000 True'. Ma comunque, è vero, che non dovremmo fare affidamento su quel @stalk – stalk

+3

Come ho detto, ** da Python 3.3 **, il comportamento è cambiato. Vedi anche ['oggetto .__ hash__'] (https://docs.python.org/3/reference/datamodel.html#object.__hash__):" La modifica dei valori hash influisce sull'ordine di iterazione di dict, set e altri mapping. * * Python non ha mai fornito garanzie su questo ordine ** (e varia tipicamente tra i build a 32 e 64 bit). " (la mia enfasi) –

+3

È strano che sia vero che molte volte. –

6

I set non sono ordinati e sono definiti solo dalla loro appartenenza.

Ad esempio, set([1, 2]) == set([2, 1])

tuple sono uguali se i loro membri a ciascuna posizione sono uguali, ma poiché le collezioni tuple sono stati creati da iterate ugualmente (in ordine crescente), tuple finiscono per essere uguale pure.

5

quindi hai due elenchi, che hanno lo stesso contenuto ma in ordini diversi, li converti in set, che saranno uguali, poiché hanno lo stesso contenuto.

Quando convertite questi set in tuple, verranno convertiti nello stesso ordine, poiché sono lo stesso set, quindi le tuple saranno le stesse.

Questo è vero in Python2.7 - ma dal 3.3 in poi, quando gli hash sono randomizzati non sarà in grado di garantire questo - come le due serie, anche se uguali nei contenuti solito neccessarily iterare nello stesso ordine.

+0

perché il downvote? –

+0

Non ho fatto downvot, ma la tua risposta non è corretta, indipendentemente dalla casualità dell'hash: ad esempio, in ogni Python dal 2.4 (quando 'set' è diventato un builtin),' tuple (set ([0, 8]))! = tuple (set ([8, 0])) '(e lo stesso vale per' sets.Set' in Python 2.3). –

+0

quindi un set non fa quello che dovrebbe fare il set - cioè essere uguale quando i contenuti sono uguali - molto strano. –

13

Ci sono due cose in gioco qui.

  1. Gli insiemi non sono ordinati. set([1, "a", "b", "c", "z", "f"])) == set(["a", "b", "c", "z", "f", 1])

  2. Quando si converte un set in una tupla tramite il costruttore tuple, itera essenzialmente sul set e aggiunge ogni elemento restituito dall'iterazione.

La sintassi costruttore di tuple è

tuple(iterable) -> tuple initialized from iterable's items 

Calling tuple(set([1, "a", "b", "c", "z", "f"])) è lo stesso di chiamare tuple([i for i in set([1, "a", "b", "c", "z", "f"])])

I valori per

[i for i in set([1, "a", "b", "c", "z", "f"])] 

e

[i for i in set(["a", "b", "c", "z", "f", 1])] 

sono gli stessi che itera sullo stesso set.

EDIT grazie a @ZeroPiraeus (controllare il suo answer). Questo non è garantito. Il valore dell'iterazione non sarà sempre lo stesso anche per lo stesso set.

Il costruttore di tuple non conosce l'ordine in cui è stato creato l'insieme.

+1

Quindi è garantito che se i valori in due 'set' sono gli stessi, verranno ripetuti nello stesso ordine? – stalk

+3

@stalk ** no **, non lo è: vedere [la mia risposta] (http://stackoverflow.com/a/26116307/1014938). –

+2

Dovresti aggiornare la risposta come per la risposta di @ ZeroPiraeus - "I valori per _ e _ sono gli stessi che scorre sullo stesso set" non è sempre vero. – icedtrees

Problemi correlati