Dzisiejszy wpis dotyczy sprawdzania równości iterowalnych obiektów. Ten temat uzupełnia poniższy filmik:

Na samym początku warto zadać sobie pytanie: Czy dobrym pomysłem będzie tworzenie listy z iterowalnego obiektu?

Plusem tego podejścia jest niewątpliwie fakt, że Python wspiera porównywanie list.

Wadą tego rozwiązania jest konieczność przetworzenia wszystkich elementów z iterowalnego obiektu, aby w efekcie końcowym stworzyć listę.

Drugie pytanie jakie może nasunąć się to: Czy iteracja z indeksami nie będzie wystarczająca? Tak jak przedstawia to przykład poniżej:

def equals(xs, ys):
    if len(xs) != len(ys):
        return False
    for i in range(len(xs)):
        if xs[i] != ys[i]:
            return False
    return True

Podejście z indeksami niestety wprowadza pewne ograniczenie. Tylko niektóre typy danych obsługują dostęp przez indeks. Operacje na typach danych takich jak kolejka czy generator będą prowadzić do  błędu, ponieważ nie wspierają bezpośredniego dostępu z użyciem indeksu (eng. random access).

Kod, który został omówiony w filmiku nie jest oczywiście jedynym możliwym rozwiązaniem. Dzięki zip_longest możemy wyprowadzić alternatywną implementację, która jest nie tylko krótsza, ale również prostsza w zrozumieniu.

zip_longest, w odróżnieniu od zip, pozwala na przekazanie wartości (fillvalue) do podstawienia, w sytuacji gdy jedna z sekwencji zakończy się wcześniej.

from itertools import zip_longest

def equals(xs, ys):
    for x, y in zip_longest(xs, ys, fillvalue=object()):
        if x != y:
            return False
    return True

Powyższa funkcja używa wywołania object() i tak jak na filmiku pełni rolę prywatnej wartości oznaczającej koniec przetwarzanej sekwencji.
W przypadku fillvalue ryzykowne jest użycie None, ponieważ None równie dobrze może być elementem sekwencji po której iterujemy.