2012-04-02 3 views
21

Я интересно об использовании == при сравнении два генераторовСравнение двух генераторов в Python

Например:

x = ['1','2','3','4','5'] 

gen_1 = (int(ele) for ele in x) 
gen_2 = (int(ele) for ele in x) 

gen_1 и gen_2 являются одинаковыми для всех практических целей, и но когда я их сравниваю:

>>> gen_1 == gen_2 
False 

Мое предположение, что == здесь рассматривается как is обычно есть, а так как gen_1 и gen_2 расположены в разных местах в памяти:

>>> gen_1 
<generator object <genexpr> at 0x01E8BAA8> 
>>> gen_2 
<generator object <genexpr> at 0x01EEE4B8> 

их сравнение имеет значение False. Правильно ли я думаю об этом? И любое другое понимание приветствуется.

И кстати, я не знаю, как сравнить два генератора:

>>> all(a == b for a,b in zip(gen_1, gen_2)) 
True 

или даже

>>> list(gen_1) == list(gen_2) 
True 

Но если есть лучший способ, я хотел бы знать.

+5

Думайте о выражении генератора как о функции, а не как о списке. – agf

+1

как только вы сравниваете генераторы, вы их исчерпываете, а затем они становятся пустыми. –

ответ

12

Вы правы с вашей догадкой - резерв для сравнения типов, которые не определяют ==, является сопоставлением, основанным на идентификаторе объекта.

Лучший способ для сравнения значений они производят бы

from itertools import izip_longest, tee 
sentinel = object() 
all(a == b for a, b in izip_longest(gen_1, gen_2, fillvalue=sentinel)) 

Это может на самом деле короткого замыкания без того, чтобы смотреть на все ценности. Как отмечают larsmans в комментариях, мы не можем использовать здесь izip(), так как это может привести к неправильным результатам, если генераторы производят другое количество элементов - izip() остановится на самом коротком итераторе. Мы используем только что созданный экземпляр object как значение заполнения для izip_longest(), так как экземпляры object также сравниваются по идентификатору объекта, поэтому sentinel гарантированно сравнится неравномерно со всем остальным.

Обратите внимание, что нет никакого способа сравнить генераторы без изменения их состояния. Вы можете хранить предметы, которые были потребляются, если вам нужно их позже:

gen_1, gen_1_teed = tee(gen_1) 
gen_2, gen_2_teed = tee(gen_2) 
all(a == b for a, b in izip_longest(gen_1, gen_2, fillvalue=sentinel)) 

Это даст покинуть состояние gen_1 и gen_2 по существу без изменений.Все значения, потребляемые all(), хранятся внутри объекта tee.

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

+0

'izip' имеет такую ​​же проблему, как' zip': при этом не удается создать одинаковое количество элементов. –

+0

@larsmans: Спасибо, я только что заметил! –

+0

Спасибо большое Свен! – Akavall

7

Поскольку генераторы генерируют свои значения по требованию, нет никакого способа «сравнить» их без фактического , потребляющих им. И если ваши генераторы генерируют бесконечную последовательность значений, такой тест равенства, который вы предлагаете, будет бесполезным.

4

Для того чтобы сделать сравнение двух генераторов по вкусу, как со списками и другими контейнерами, Python должен был бы использовать их как полностью (ну, тем более короче, так или иначе). Я думаю, что хорошо, что вы должны делать это явно, тем более, что один или другой может быть бесконечным.

5

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

list(gen_1) == list(gen_2) 

- надежный и общий способ сравнения двух конечных генераторов (но, очевидно, потребляет оба); ваше решение основанное zip терпит неудачу, когда они не создают Равное количество элементов:

>>> list(zip([1,2,3,4], [1,2,3])) 
[(1, 1), (2, 2), (3, 3)] 
>>> all(a == b for a, b in zip([1,2,3,4], [1,2,3])) 
True 

основанное решение list до сих пор не удается, когда либо генератор генерирует бесконечное число элементов. Вы можете придумать обходной путь для этого, но когда оба генератора бесконечны, вы можете создать только semi-algorithm для неравновесия.

+0

Спасибо, что указали на ошибку в моем решении на основе zip. – Akavall

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