607 Stimmen

Ermittelt das erste Element aus einer Iterablen, das einer Bedingung entspricht

Ich möchte das erste Element aus einer Liste abrufen, das einer Bedingung entspricht. Es ist wichtig, dass die resultierende Methode nicht die gesamte Liste verarbeitet, die ziemlich groß sein könnte. Die folgende Funktion ist zum Beispiel geeignet:

def first(the_iterable, condition = lambda x: True):
    for i in the_iterable:
        if condition(i):
            return i

Diese Funktion könnte etwa so verwendet werden:

>>> first(range(10))
0
>>> first(range(10), lambda i: i > 3)
4

Allerdings fällt mir kein guter eingebauter / Einzeiler ein, mit dem ich das tun könnte. Ich möchte nicht unbedingt diese Funktion herumkopieren, wenn ich nicht muss. Gibt es einen eingebauten Weg, um das erste Element mit einer Bedingung zu erhalten?

8 Stimmen

927voto

Alex Martelli Punkte 805329

Python 2.6+ und Python 3:

Wenn Sie wollen StopIteration ausgelöst werden, wenn kein passendes Element gefunden wird:

next(x for x in the_iterable if x > 3)

Wenn Sie wollen default_value (z.B.. None ) zurückgegeben werden:

next((x for x in the_iterable if x > 3), default_value)

Beachten Sie, dass Sie ein zusätzliches Klammerpaar um den Generatorausdruck herum benötigen. In diesem Fall werden sie immer dann benötigt, wenn der Generatorausdruck nicht das einzige Argument ist.

Ich sehe, dass die meisten Antworten die next eingebaut und so nehme ich an, dass sie sich aus irgendeinem mysteriösen Grund zu 100% auf die Versionen 2.5 und älter konzentrieren -- ohne das Problem der Python-Version zu erwähnen (aber ich sehe diese Erwähnung auch nicht in den Antworten, die faire erwähnen die next eingebaut, weshalb ich es für notwendig erachtete, selbst eine Antwort zu geben - zumindest wird die Frage der "richtigen Version" auf diese Weise festgehalten;-).

Python <= 2.5

Le site .next() Methode von Iteratoren führt sofort zu StopIteration wenn der Iterator sofort beendet wird, d.h. für Ihren Anwendungsfall, wenn kein Element in der Iterable die Bedingung erfüllt. Wenn es Sie nicht interessiert (d.h. Sie wissen, dass es debe mindestens ein zufriedenstellendes Element sein), dann verwenden Sie einfach .next() (am besten auf einem Genexp, Linie für die next eingebaut in Python 2.6 und besser).

Wenn Sie faire Sorgfalt, die Dinge in eine Funktion zu verpacken, wie Sie in Ihrem Q zuerst angedeutet hatten, scheint am besten zu sein, und während die von Ihnen vorgeschlagene Funktionsimplementierung genau richtig ist, könnten Sie alternativ auch itertools , a for...: break Schleife, oder ein genexp, oder ein try/except StopIteration als Körper der Funktion, wie verschiedene Antworten nahelegten. Es gibt nicht viel Mehrwert in einer dieser Alternativen, so würde ich für die stark vereinfachte Version gehen Sie zuerst vorgeschlagen.

6 Stimmen

Es funktioniert nicht so, wie Sie es beschreiben. Es hebt StopIteration wenn kein Element gefunden wurde

0 Stimmen

Da dies in den Suchergebnissen auftaucht, habe ich den Kommentar von @Suor aus dem Jahr 2011 befolgt und den ersten Absatz ein wenig umformuliert, um die Dinge klarer zu machen. Bitte machen Sie weiter und ändern Sie meine Änderung, wenn Sie es brauchen.

7 Stimmen

Da dies die gewählte Antwort ist, fühle ich mich gezwungen, eine Antwort für die richtige Auswahl des ersten Elements zu geben aquí . Kurzum: Die Verwendung von next sollte nicht gefördert werden.

75voto

Verdammte Ausnahmen!

Ich liebe diese Antwort . Da jedoch next() einen StopIteration Ausnahme, wenn es keine Artikel gibt, würde ich das folgende Snippet verwenden, um eine Ausnahme zu vermeiden:

a = []
item = next((x for x in a), None)

Zum Beispiel,

a = []
item = next(x for x in a)

Erhöht eine StopIteration Ausnahme;

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

49voto

Caridorc Punkte 3671

Als wiederverwendbare, dokumentierte und getestete Funktion

def first(iterable, condition = lambda x: True):
    """
    Returns the first item in the `iterable` that
    satisfies the `condition`.

    If the condition is not given, returns the first item of
    the iterable.

    Raises `StopIteration` if no item satysfing the condition is found.

    >>> first( (1,2,3), condition=lambda x: x % 2 == 0)
    2
    >>> first(range(3, 100))
    3
    >>> first( () )
    Traceback (most recent call last):
    ...
    StopIteration
    """

    return next(x for x in iterable if condition(x))

Version mit Standardargument

@zorf schlug eine Version dieser Funktion vor, bei der man einen vordefinierten Rückgabewert haben kann, wenn die iterable leer ist oder keine der Bedingung entsprechenden Elemente enthält:

def first(iterable, default = None, condition = lambda x: True):
    """
    Returns the first item in the `iterable` that
    satisfies the `condition`.

    If the condition is not given, returns the first item of
    the iterable.

    If the `default` argument is given and the iterable is empty,
    or if it has no items matching the condition, the `default` argument
    is returned if it matches the condition.

    The `default` argument being None is the same as it not being given.

    Raises `StopIteration` if no item satisfying the condition is found
    and default is not given or doesn't satisfy the condition.

    >>> first( (1,2,3), condition=lambda x: x % 2 == 0)
    2
    >>> first(range(3, 100))
    3
    >>> first( () )
    Traceback (most recent call last):
    ...
    StopIteration
    >>> first([], default=1)
    1
    >>> first([], default=1, condition=lambda x: x % 2 == 0)
    Traceback (most recent call last):
    ...
    StopIteration
    >>> first([1,3,5], default=1, condition=lambda x: x % 2 == 0)
    Traceback (most recent call last):
    ...
    StopIteration
    """

    try:
        return next(x for x in iterable if condition(x))
    except StopIteration:
        if default is not None and condition(default):
            return default
        else:
            raise

8 Stimmen

Wenn Sie es mit einer Methode verpacken, zumindest fangen StopIteration und EmptySequence Fehler auslösen. Wäre viel hübscher, wenn es keine Elemente gibt.

0 Stimmen

@guyarad Ist das eine Art von ValueError?

0 Stimmen

Nein... Ich habe gerade eine Ausnahme "erfunden". Wenn Sie eine first Funktion, indem sie eine StopIteration Ausnahme scheint einfach seltsam zu sein. Wie auch immer, werfen Sie einen Blick auf boltons.iterutils.first .

30voto

Mariano Ruiz Punkte 3816

Der effizienteste Weg in Python 3 ist einer der folgenden (anhand eines ähnlichen Beispiels):

Mit "Verstehen" Stil:

next(i for i in range(100000000) if i == 1000)

WARNUNG : Der Ausdruck funktioniert auch mit Python 2, aber im Beispiel wird verwendet range das in Python 3 ein iterierbares Objekt zurückgibt, anstatt einer Liste wie in Python 2 (wenn Sie eine iterierbare Datei in Python 2 erstellen wollen, verwenden Sie xrange stattdessen).

Beachten Sie, dass der Ausdruck vermeiden, um eine Liste in der comprehension Ausdruck zu konstruieren next([i for ...]) Das würde dazu führen, dass eine Liste mit allen Elementen erstellt wird, bevor die Elemente gefiltert werden, und würde dazu führen, dass die gesamten Optionen verarbeitet werden, anstatt die Iteration einmal anzuhalten i == 1000 .

Mit "funktionell" Stil:

next(filter(lambda i: i == 1000, range(100000000)))

WARNUNG : Das funktioniert in Python 2 nicht, auch nicht beim Ersetzen von range con xrange aufgrund dessen filter eine Liste anstelle eines Iterators erstellen (ineffizient), und die next Funktion funktioniert nur mit Iteratoren.

Standardwert

Wie bereits in anderen Antworten erwähnt, müssen Sie der Funktion einen Extra-Parameter hinzufügen next wenn Sie eine Ausnahme vermeiden wollen, die ausgelöst wird, wenn die Bedingung nicht erfüllt ist.

"funktionell" Stil:

next(filter(lambda i: i == 1000, range(100000000)), False)

"Verstehen" Stil:

Bei diesem Stil müssen Sie den Ausdruck "comprehension" mit () zur Vermeidung einer SyntaxError: Generator expression must be parenthesized if not sole argument :

next((i for i in range(100000000) if i == 1000), False)

0 Stimmen

Python hat keinen funktionalen Stil. Es ist eher ein hässlicher statemantaler Stil :P

26voto

airborne Punkte 2984

Für alle, die Python 3.8 oder neuer verwenden, empfehle ich die Verwendung von "Assignment Expressions", wie sie in PEP 572 -- Zuweisungsausdrücke .

if any((match := i) > 3 for i in range(10)):
    print(match)

1 Stimmen

Das ist ein wirklich großartiger Einsatz des Walross-Operators, sehr elegant.

1 Stimmen

Bei leerer Liste ist die Übereinstimmung nicht definiert

1 Stimmen

@SmartManoj: Natürlich ist match nicht für eine leere Liste definiert. Welchen Wert würden Sie erwarten? Das Gleiche gilt für jede andere Liste, die kein Element enthält, das der Bedingung entspricht. Worauf willst du hinaus?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X