Ich frage mich, ob es einen Grund gibt, warum es first(iterable)
in den in Python integrierten Funktionen keine gibt , ähnlich wie any(iterable)
und all(iterable)
(es kann irgendwo in einem stdlib-Modul versteckt sein, aber ich sehe es nicht in itertools
). first
würde eine Kurzschlussgeneratorauswertung durchführen, so dass unnötige (und möglicherweise unendlich viele) Operationen vermieden werden können; dh
def identity(item):
return item
def first(iterable, predicate=identity):
for item in iterable:
if predicate(item):
return item
raise ValueError('No satisfactory value found')
Auf diese Weise können Sie Dinge ausdrücken wie:
denominators = (2, 3, 4, 5)
lcd = first(i for i in itertools.count(1)
if all(i % denominators == 0 for denominator in denominators))
Offensichtlich können Sie nicht tun , list(generator)[0]
in diesem Fall, da der Generator nicht beendet.
Oder wenn Sie eine Reihe von regulären Ausdrücken haben, mit denen Sie übereinstimmen können (nützlich, wenn alle dieselbe groupdict
Benutzeroberfläche haben):
match = first(regex.match(big_text) for regex in regexes)
Sie sparen viel unnötige Verarbeitung, indem Sie list(generator)[0]
eine positive Übereinstimmung vermeiden und kurzschließen.
Antworten:
Wenn Sie einen Iterator haben, können Sie einfach dessen
next
Methode aufrufen . Etwas wie:In [3]: (5*x for x in xrange(2,4)).next() Out[3]: 10
quelle
next(x)
wennx
es sich um einen Iterator handelt odernext(iter(d))
wennd
es iterierbar istnext(iter(xs))
. In Python 2.5 können Sie dies tuniter(xs).next()
.itertools.ifilter()
oder(… for … in … if condition)
Kombinieren) nicht ausreichen, um die Verwendung eines anderen integrierten Tools zu rechtfertigen. Beachten Sie, dass das Regex-Beispiel von OP nurnext(regex for regex in regexes if regex.match(big_text))
.next(regex for regex in regexes if regex.match(big_text))
; Es gibt die Ergebnisse von regex.match (big_text) zurück. Wie macht man das ohne first ()?next(regex.match(big_text) for regex in regexes if regex.match(big_text))
ist redundant.next(ifilter(imap(lambda x: x.match(big_text), regexes)))
scheint im Vergleich zu zuerst zu komplex.Es gibt ein Pypi-Paket namens "first" , das dies tut:
>>> from first import first >>> first([0, None, False, [], (), 42]) 42
So würden Sie beispielsweise die erste ungerade Zahl zurückgeben:
>> first([2, 14, 7, 41, 53], key=lambda x: x % 2 == 1) 7
Wenn Sie nur das erste Element vom Iterator zurückgeben möchten, unabhängig davon, ob es wahr ist oder nicht, gehen Sie folgendermaßen vor:
>>> first([0, None, False, [], (), 42], key=lambda x: True) 0
Es ist ein sehr kleines Paket: Es enthält nur diese Funktion, hat keine Abhängigkeiten und funktioniert unter Python 2 und 3. Es ist eine einzelne Datei, sodass Sie sie nicht einmal installieren müssen, um sie zu verwenden.
Tatsächlich ist hier fast der gesamte Quellcode (ab Version 2.0.1 von Hynek Schlawack, veröffentlicht unter der MIT-Lizenz):
def first(iterable, default=None, key=None): if key is None: for el in iterable: if el: return el else: for el in iterable: if key(el): return el return default
quelle
first
Funktion vom Modul aus implementierenfirst
? Wenn ich frage, warum dies nicht integriert ist, mache ich das, weil ich vermute, dass es eine pythonische Möglichkeit gibt, dies mit allgemeineren Funktionen wie Listenverständnis usw. auszudrücken, die es überflüssig genug machen, es wegzulassen.first
nützlich finden werden . Ich habe den Quellcode für die Funktion für Interesse aufgenommen.Ich habe kürzlich eine ähnliche Frage gestellt (sie wurde inzwischen als Duplikat dieser Frage markiert). Meine Sorge war auch, dass ich eingebaute Geräte nur verwenden wollte , um das Problem zu lösen, den ersten wahren Wert eines Generators zu finden. Meine eigene Lösung war dann folgende:
x = next((v for v in (f(x) for x in a) if v), False)
Für das Beispiel des Findens der ersten regulären Ausdrucksübereinstimmung (nicht des ersten übereinstimmenden Musters!) Würde dies folgendermaßen aussehen:
patterns = [ r'\d+', r'\s+', r'\w+', r'.*' ] text = 'abc' firstMatch = next( (match for match in (re.match(pattern, text) for pattern in patterns) if match), False)
Das Prädikat wird nicht zweimal ausgewertet (wie Sie es tun müssten, wenn nur das Muster zurückgegeben würde), und es werden keine Hacks wie Einheimische im Verständnis verwendet.
Es sind jedoch zwei Generatoren verschachtelt, bei denen die Logik vorschreibt, nur einen zu verwenden. Eine bessere Lösung wäre also schön.
quelle
Es gibt einen "Slice" -Iterator in itertools. Es emuliert die Slice-Operationen, mit denen wir in Python vertraut sind. Was Sie suchen, ist etwas Ähnliches:
myList = [0,1,2,3,4,5] firstValue = myList[:1]
Das Äquivalent mit itertools für Iteratoren:
from itertools import islice def MyGenFunc(): for i in range(5): yield i mygen = MyGenFunc() firstValue = islice(mygen, 0, 1) print firstValue
quelle
Ihre Frage enthält einige Unklarheiten. Ihre Definition von first und das Regex-Beispiel implizieren, dass es einen booleschen Test gibt. Das Nennerbeispiel enthält jedoch explizit eine if-Klausel. Es ist also nur ein Zufall, dass jede ganze Zahl wahr ist.
Es sieht so aus, als ob die Kombination von next und itertools.ifilter Ihnen das gibt, was Sie wollen.
match = next(itertools.ifilter(None, (regex.match(big_text) for regex in regexes)))
quelle
next(iterator)
war die Antwort, die mir fehlte.Haskell verwendet das, was Sie gerade beschrieben haben, als Funktion
take
(odertake 1
technisch als Teilfunktion ). In Python Cookbook sind Generator-Wrapper geschrieben, die dieselbe Funktionalität wie ausführentake
,takeWhile
unddrop
in Haskell.Aber warum das nicht eingebaut ist, ist Ihre Vermutung so gut wie meine.
quelle