2015-10-12 12 views
6

Recentemente mi sono imbattuto in cosmologicon di pywats e ora cercano di capire parte circa divertimento con iteratori:tipi iterabili comprensione confronti

>>> a = 2, 1, 3 
>>> sorted(a) == sorted(a) 
True 
>>> reversed(a) == reversed(a) 
False 

Ok, sorted(a) restituisce un list e sorted(a) == sorted(a) diventa solo un confronto di due liste. Ma reversed(a) restituisce reversed object. Allora perché questi oggetti invertiti sono diversi? E confronto di id mi rende ancora più confusa:

>>> id(reversed(a)) == id(reversed(a)) 
True 
+2

Gli iteratori in genere si confrontano con False, perché non puoi sapere cosa otterrai dal passare sopra a loro fino a quando non lo fai. – jonrsharpe

+0

Quindi il confronto restituisce False a causa dell'implementazione inversa di '__eq__'? – valignatev

+0

'sorted' restituisce una lista. Un confronto più equo sarebbe tra 'iter (a)' e 'invertito (a)'. – wim

risposta

10

La ragione fondamentale per cui id(reversed(a) == id(reversed(a) rendimenti True, mentre reversed(a) == reversed(a) rendimenti False, può essere visto da l'esempio di seguito utilizzando classi personalizzate -

>>> class CA: 
...  def __del__(self): 
...    print('deleted', self) 
...  def __init__(self): 
...    print('inited', self) 
... 
>>> CA() == CA() 
inited <__main__.CA object at 0x021B8050> 
inited <__main__.CA object at 0x021B8110> 
deleted <__main__.CA object at 0x021B8050> 
deleted <__main__.CA object at 0x021B8110> 
False 
>>> id(CA()) == id(CA()) 
inited <__main__.CA object at 0x021B80F0> 
deleted <__main__.CA object at 0x021B80F0> 
inited <__main__.CA object at 0x021B80F0> 
deleted <__main__.CA object at 0x021B80F0> 
True 

Come si può vedere quando hai fatto customobject == customobject, l'oggetto che è stato creato al volo non è stato distrutto fino a dopo la il confronto si è verificato, questo perché l'oggetto era richiesto per il confronto.

Ma in caso di id(co) == id(co), l'oggetto personalizzato creato è stato passato alla id() funzione, e quindi solo il risultato di id funzione è necessaria per il confronto, quindi l'oggetto creato ha alcun riferimento a sinistra, e quindi l'oggetto era raccolta dei rifiuti, e quindi quando l'interprete Python ha ricreato un nuovo oggetto per il lato destro dell'operazione ==, ha riutilizzato lo spazio precedentemente liberato. Quindi, il id per entrambi è venuto come lo stesso.

Questo comportamento sopra riportato è un dettaglio di implementazione di CPython (potrebbe/potrebbe non differire in altre implementazioni di Python). E non dovresti mai fare affidamento sull'eguaglianza di ids. Ad esempio, nel caso di sotto di essa dà il risultato sbagliato -

>>> a = [1,2,3] 
>>> b = [4,5,6] 
>>> id(reversed(a)) == id(reversed(b)) 
True 

La ragione di questo è ancora una volta come spiegato sopra (garbage collection dell'oggetto reversed creata per reversed(a) prima della creazione dell'oggetto invertita per reversed(b)).


Se le liste sono grandi, penso che il più efficiente della memoria e molto probabilmente il metodo più veloce per confrontare l'uguaglianza per due iteratori sarebbe quella di utilizzare all() funzione built-in insieme a zip() funzione per Python 3.x (oppure itertools.izip() per Python 2.x).

Esempio Python 3.x -

all(x==y for x,y in zip(aiterator,biterator)) 

Esempio Python 2.x -

from itertools import izip 
all(x==y for x,y in izip(aiterator,biterator)) 

Questo perché all() cortocircuiti il ​​primo valore False è incontri, e `zip () in Python 3.x restituisce un iteratore che restituisce gli elementi corrispondenti da entrambi i diversi iteratori. Questo non ha bisogno di creare un elenco separato in memoria.

Demo -

>>> a = [1,2,3] 
>>> b = [4,5,6] 
>>> all(x==y for x,y in zip(reversed(a),reversed(b))) 
False 
>>> all(x==y for x,y in zip(reversed(a),reversed(a))) 
True 
+2

OK! Questo spiega perché 'x = invertito (a)' 'y = invertito (a)' 'id (x) == id (y)' dà 'False'. Grazie. – Pynchia

+0

Ora ha decisamente senso per me! Quindi 'reverseed (a) == reverseed (a)' è in realtà un confronto tra due oggetti diversi! – valignatev

+1

@valentjedi: si noti che il comportamento di 'id()' è specifico di CPython. Non fare affidamento su di esso se vuoi che il tuo codice sia portatile e compatibile con le future versioni di CPython. –

7

sorted restituisce una lista, mentre reversed restituisce un oggetto reversed ed è un oggetto diverso. Se dovessi trasmettere il risultato di reversed a un elenco prima del confronto, saranno uguali.

In [8]: reversed(a) 
Out[8]: <reversed at 0x2c98d30> 

In [9]: reversed(a) 
Out[9]: <reversed at 0x2c989b0> 
0

Puoi provare list(reversed(a)) ==list(reversed(a)) tornerà True

list(reversed(a)) 
[3, 2, 1] 

una volta provare

>>> v = id(reversed(a)) 
>>> n = id(reversed(a)) 
>>> v == n 
False 

nuovo

>>> v = id(reversed(a)) 
>>> n = id(reversed(a)) 
>>> n1 = id(reversed(a)) 
>>> v == n1 
True 
+1

'v == n1' ==>' Vero? come? La tua risposta difficilmente spiega qualcosa. –

+0

@AshwiniChaudhary prova 'id (reverse (a))' continuamente da 5 a 10 volte in Python Shell. Otterrai qualche ripetizione di ID – Ravichandra

+0

Questo perché, a causa della raccolta dati obsoleti di oggetti precedenti, CPython può riutilizzare quello spazio di memoria, se ci sono già riferimenti a un oggetto, allora un nuovo oggetto utilizzerà un ID diverso. –

3

reversed restituisce un iterabile che non implementa una specifica __eq__ operatore ed è quindi confrontata con l'identità.

La confusione riguardo id(reversed(a)) == id(reversed(a)) è perché dopo aver valutato la prima id(...) chiamata iterabile può essere disposto (nulla riferimento) e la seconda iterabile può essere riassegnato allo stesso indirizzo di memoria quando il secondo id(...) chiamata è fatto. Questa è comunque solo una coincidenza.

Prova

ra1 = reversed(a) 
ra2 = reversed(a) 

e confrontare id(ra1) con id(ra2) e vedrete che sono numeri diversi (perché in questo caso gli oggetti iterabili non possono essere deallocate come sono referenziate da ra1/ra2 variabili).