2015-10-12 3 views
6

Недавно я столкнулся с cosmologicon-х pywats и теперь пытаемся понять часть о веселье с итераторами:Понимание Iterable типов в сравнениях

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

Ok, sorted(a) возвращает list и sorted(a) == sorted(a) становится только два списка Comparision. Но reversed(a) возвращает reversed object. Итак, почему эти обратные объекты разные? И Comparision идентификаторов делает меня еще более запутанным:

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

Итераторы обычно сравниваются как False, потому что вы не можете знать, что вы получите от итерации над ними, пока вы это сделаете. – jonrsharpe

+0

Итак, сравнение возвращает False из-за обратной реализации '__eq__'? – valignatev

+0

'sorted' возвращает список. Более справедливое сравнение было бы между «iter (a)» и «обратным (a)». – wim

ответ

10

Основная причина, почему id(reversed(a) == id(reversed(a) возвращает True, в то время как reversed(a) == reversed(a) возвращается False, можно видеть из приведенного ниже примера, используя пользовательские классы -

>>> 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 

Как вы можете видеть, когда вы сделали customobject == customobject объект, который был создан на лету не был разрушен до после сравнение произошло, это потому, что этот объект был необходим для сравнения.

Но в случае id(co) == id(co), пользовательский объект, созданный был передан id() функции, а затем только результат id функции требуется для сравнения, так что объект, который был создан не имеет никаких ссылок слева, и, следовательно, объект был сбор мусора, а затем, когда интерпретатор Python воссоздал новый объект для правой стороны операции ==, он снова использовал освобожденное пространство. Следовательно, id для обоих были такими же.

Данное поведение является частью реализации CPython (она может/не может отличаться в других реализациях Python). И вы никогда не должны полагаться на равенство ids. Например, в случае ниже дает неправильный результат -

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

Причина этого опять-таки, как описано выше (вывоз мусора из reversed объекта, созданного для reversed(a) до создания реверсивного объекта для reversed(b)).


Если списки большие, я думаю, что самая память эффективная и наиболее вероятно, самый быстрый способ для сравнения равенства двух итераторов была бы использовать all() встроенной функции наряду с zip() функции для Python 3.x (или itertools.izip() для Python 2.x).

Пример для Python 3.x -

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

Пример для Python 2.x -

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

Это происходит потому, all() коротких замыкания на первое ложного значения является столкновением, и `молнии () в Python 3.x возвращает итератор, который выдает соответствующие элементы из разных итераторов. Это не нужно создавать отдельный список в памяти.

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

Хорошо! Это объясняет, почему 'x = reverse (a)' y = reverse (a) '' id (x) == id (y) 'дает' False'. Спасибо. – Pynchia

+0

Теперь это определенно имеет смысл для меня! Поэтому 'reverse (a) == reverseed (a)' фактически представляет собой сравнение двух разных объектов! – valignatev

+1

@valentjedi: обратите внимание, что поведение для 'id()' специфично для CPython. Не полагайтесь на него, если вы хотите, чтобы ваш код был портативным и совместимым с будущими версиями CPython. –

7

sorted возвращает список, в то время как reversed возвращает reversed объект и другой объект. Если вы должны были передать результат reversed в список перед сравнением, они будут равны.

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

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

Вы можете попробовать list(reversed(a)) ==list(reversed(a)) возвратит True

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

раз попробовать

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

снова

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

'v == n1' ==>' True' как? Ваш ответ почти ничего не объясняет. –

+0

@AshwiniChaudhary try 'id (reverse (a))' непрерывно в течение 5-10 раз в оболочке python. вы получите некоторое повторение идентификатора ID – Ravichandra

+0

Это связано с тем, что из-за сбора мусора старых объектов CPython может повторно использовать это пространство памяти, если уже есть ссылка на объект, тогда новый объект будет использовать другой идентификатор. –

3

reversed возвращает итератор, который не реализует конкретный __eq__ оператор и, следовательно, по сравнению с использованием идентичности.

Смешение о id(reversed(a)) == id(reversed(a)) происходит потому, что после оценки первого id(...) Вызвать итерацию может быть расположен (ничто не ссылается его), а второй Iterable может быть перераспределена в тот же самый адрес памяти, когда второй id(...) вызов делается. Это, однако, просто совпадение.

Попробуйте

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

и сравнить id(ra1) с id(ra2), и вы увидите, что они разные номера (потому что в этом случае Iterable объекты не могут быть высвобождены, как они ссылаются ra1/ra2 переменных).

Смежные вопросы