Zwei iterators parallel durchlaufen, Fehler bei ungleicher Länge

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Mick
User
Beiträge: 9
Registriert: Mittwoch 8. Januar 2020, 09:12

Hey Leute,
ich möchte zwei iterators parallel durchlaufen. Beide sollten im Normalfall gleich lang sein und daher gleichzeitig leer sein. Ich möchte, dass der iterator automatisch einen Fehler ausgibt, wenn beide Subiteratoren nicht gleich lang sind, damit ich das später im Code nicht mehr überprüfen muss.

Ich habe diese Lösung hier gefunden, mit zip_longest aus den itertools:
https://stackoverflow.com/questions/329 ... -in-python

Code: Alles auswählen

from itertools import zip_longest

def zip_equal(*iterables):
    sentinel = object()
    for combo in zip_longest(*iterables, fillvalue=sentinel):
        if any([c is sentinel for c in combo]):
            raise ValueError('Iterables have different lengths')
        yield combo
Kann mir jemand verraten warum ich dafür das sentinel object benötige? Warum kann ich nicht einfach die Standard fillvalue 'None' verwenden und das überprüfen? So:

Code: Alles auswählen

def zip_equal(*iterables):
    for combo in zip_longest(*iterables):
        if any([c is None for c in combo]):
            raise ValueError('Iterables have different lengths')
        yield combo
Danke!
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Weil du dann sicherstellen musst, das None niemals ein valides Ergebnis eines der iteratoren ist. Womit das Rezept nicht wirklich generisch wäre.
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

Mir gefällt ja die zweite Lösung deutlich besser.
@Mick: die eckigen Klammern bei `any` sind überflüssig, und machen die Nutzung von any auch unelegant.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die zweite macht halt nicht das gleiche wie die erste - wenn man sich das erlauben kann ist ja gut, aber die erste ist ganz klar der Kandidat fuer die Sammlung fuer hilfreichen Kram.

Was mir an beiden nicht gefaellt ist die unnoetige list-comprehension im any-clause - da haette ich einen generator-Ausdruck gebaut.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

So waere mein Entwurf, inklusive der Moeglichkeit den sentinel zu modifizieren:

Code: Alles auswählen

from itertools import zip_longest, chain, cycle

def zip_equal(*iterables, sentinel=object()):
    for combo in zip_longest(*iterables, fillvalue=sentinel):
        if any(c is sentinel for c in combo):
            raise ValueError('Iterables have different lengths')
        yield combo

for a, b in zip_equal(chain(range(3), cycle([None])), range(5)):
     print(a, b)
Mick
User
Beiträge: 9
Registriert: Mittwoch 8. Januar 2020, 09:12

Hallo an alle,
vielen Dank für die Antworten. Dann ist es für meinen use-case in Ordnung wenn ich die Funktion ohne das Sentinel-object verwende, denn None kann ich als Ergebnis der iterators ausschließen.

Danke auch für das spotten der unnötigen list-comprehension. Hab ich ausgebaut.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1240
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Manche verwenden auch ... (Ellipsis) als Sentinel. Würde ich aber nicht verwenden, da es bei den Meisten ziemlich unbekannt ist.
Da ist die Variante mit dem eigenen Objekt viel besser und wird so auch oft angewandt.

Bei multiprocessing funktioniert das leider nicht, da die Objekte in den Prozessen unterschiedliche Identitäten haben.
Mit threading funktioniert das aber schon.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
__blackjack__
User
Beiträge: 14051
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Das hier sollte auch bei multiprocessing funktionieren, denn das Sentinel-Objekt wird ja nur lokal in der Funktion verwendet und die läuft ja nicht über mehrere Prozesse verteilt.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benutzeravatar
DeaD_EyE
User
Beiträge: 1240
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

__blackjack__ hat geschrieben: Donnerstag 9. Januar 2020, 13:31 Das hier sollte auch bei multiprocessing funktionieren, denn das Sentinel-Objekt wird ja nur lokal in der Funktion verwendet und die läuft ja nicht über mehrere Prozesse verteilt.
Stimmt. Das Beispiel sollte auch mit Multiprocessing funktionieren.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Antworten