Python Idiom zurückzukehren erstes Element oder None
-
21-08-2019 - |
Frage
Ich bin sicher, dass es ein einfacherer Weg, dies zu tun, das ist einfach nicht zu mir kommt.
Ich bin ein Bündel von Methoden aufrufen, die eine Liste zurückzukehren. Die Liste kann leer sein. Wenn die Liste nicht leer ist, möchte ich das erste Element zurückzukehren; sonst möchte ich keine zurückzukehren. Dieser Code funktioniert:
my_list = get_list()
if len(my_list) > 0: return my_list[0]
return None
Es scheint mir, dass es ein einfaches einzeiliges Idiom sein, dies zu tun, sondern für das Leben von mir kann ich nicht daran denken. Gibt es?
Edit:
Der Grund, dass ich mich für einen einzeiligen Ausdruck hier auf der Suche ist nicht, dass ich unglaublich terse Code mag, sondern weil ich habe eine Menge Code wie folgen zu schreiben:
x = get_first_list()
if x:
# do something with x[0]
# inevitably forget the [0] part, and have a bug to fix
y = get_second_list()
if y:
# do something with y[0]
# inevitably forget the [0] part AGAIN, and have another bug to fix
Was möchte ich auf jeden Fall mit einer Funktion erreicht werden kann, zu tun (und wahrscheinlich wird):
def first_item(list_or_none):
if list_or_none: return list_or_none[0]
x = first_item(get_first_list())
if x:
# do something with x
y = first_item(get_second_list())
if y:
# do something with y
Ich stellt die Frage, weil ich oft überrascht bin, was einfache Ausdrücke in Python tun, und ich dachte, dass eine Funktion zu schreiben eine dumme Sache zu tun war, wenn es ein einfacher Ausdruck ist, konnte den Trick. Aber diese Antworten zu sehen, es scheint wie eine Funktion ist die einfache Lösung.
Lösung
Python 2.6 +
next(iter(your_list), None)
Wenn your_list
None
werden kann:
next(iter(your_list or []), None)
Python 2.4
def get_first(iterable, default=None):
if iterable:
for item in iterable:
return item
return default
Beispiel:
x = get_first(get_first_list())
if x:
...
y = get_first(get_second_list())
if y:
...
Eine weitere Möglichkeit ist die obige Funktion inline:
for x in get_first_list() or []:
# process x
break # process at most one item
for y in get_second_list() or []:
# process y
break
Um zu vermeiden, break
Sie schreiben können:
for x in yield_first(get_first_list()):
x # process x
for y in yield_first(get_second_list()):
y # process y
Dabei gilt:
def yield_first(iterable):
for item in iterable or []:
yield item
return
Andere Tipps
Der beste Weg ist dies:
a = get_list()
return a[0] if a else None
Sie können es auch tun, in einer Linie, aber es ist viel schwieriger für den Programmierer zu lesen:
return (get_list()[:1] or [None])[0]
(get_list() or [None])[0]
Das sollte funktionieren.
BTW habe ich nicht den Variable list
verwenden, denn das ist die eingebaute list()
Funktion überschreibt.
Edit:. Ich hatte eine etwas einfachere, aber falsche Version hier früher
Die meisten Python idiomatische Weg ist es, die nächste () auf einem Iterator zu verwenden, da die Liste ist iterable . wie, was @ J.F.Sebastian in den Kommentar setzen auf 13. Dezember 2011.
next(iter(the_list), None)
Dies gibt keine, wenn the_list
leer. finden Sie unter next () Python 2.6+
oder wenn Sie sicher the_list
wissen nicht leer ist:
iter(the_list).next()
finden Sie unter iterator.next () Python 2.2 +
Die Lösung der OP ist fast da, es gibt nur wenige Dinge, die es mehr Pythonic zu machen.
Zum einen gibt es keine Notwendigkeit, die Länge der Liste zu bekommen. Leere Listen in Python zu bewerten auf False in einem if-Check. Einfach nur sagen
if list:
Darüber hinaus ist es eine sehr schlechte Idee, um Variablen zuzuweisen, die mit reservierten Wörtern überlappen. "Liste" ist ein reserviertes Wort in Python.
Also lassen Sie uns das ändern zu
some_list = get_list()
if some_list:
Ein wirklich wichtiger Punkt, dass viele Lösungen hier fehlt, ist, dass alle Python-Funktionen / Methoden Keine Rückkehr von default . Versuchen Sie, die folgenden unten.
def does_nothing():
pass
foo = does_nothing()
print foo
Wenn Sie keine zurückkehren müssen früh, um eine Funktion zu beenden, ist es nicht erforderlich, keine explizit zurück. Ganz kurz und bündig, nur den ersten Eintrag zurückzukehren, sollte es existieren.
some_list = get_list()
if some_list:
return list[0]
Und schließlich, vielleicht war dies stillschweigend, aber nur explizit zu sein (weil explizit besser als implizite ), sollten Sie Ihre Funktion aus einer anderen Funktion erhalten Sie die Liste nicht; geben sie nur als Parameter in. So würde das Endergebnis sein
def get_first_item(some_list):
if some_list:
return list[0]
my_list = get_list()
first_item = get_first_item(my_list)
Wie gesagt, war der OP fast da, und nur wenige Berührungen geben ihm die Python Geschmack Sie suchen.
Wenn Sie sich selbst versuchen, das erste, was (oder None) aus einer Liste Verständnis zupfen Sie an einen Generator schalten kann es gerne tun:
next((x for x in blah if cond), None)
Pro: funktioniert, wenn blah nicht Wende Con ist: es ungewohnte Syntax ist. Es ist nützlich, während obwohl um und Filter Sachen in ipython Hacking.
for item in get_list():
return item
Ehrlich gesagt, ich glaube nicht, dass es eine bessere Idiom ist: Sie ist klar und prägnant - keine Notwendigkeit für etwas „besser“. Vielleicht, aber das ist wirklich eine Frage des Geschmacks ist, könnten Sie if len(list) > 0:
mit if list:
ändern -. Eine leere Liste wird immer auf False auswerten
Auf einem verwandten beachten, Python ist nicht Perl (kein Wortspiel beabsichtigt!), Sie müssen nicht den coolste Code möglich.
Tatsächlich haben die schlechteste Code, den ich in Python zu sehen ist, war auch sehr cool :-) und vollständig wartbaren.
Durch die Art und Weise, die meisten der Lösung, die ich hier gesehen habe nehme nicht in Betracht, wenn die Liste [0] das Ergebnis falsch (zB leere Zeichenkette oder null) - in diesem Fall, sie alles None zurück und nicht das richtige Element .
Python Idiom erstes Element oder None zurück?
Die meisten Pythonic Ansatz ist es, was die meisten upvoted Antwort demonstriert, und es war das erste, was mir in den Sinn zu kommen, wenn ich die Frage lesen. Hier ist, wie es zu benutzen, zuerst, wenn die möglicherweise leere Liste in eine Funktion übergeben wird:
def get_first(l):
return l[0] if l else None
Und wenn die Liste aus einer get_list
Funktion zurückgegeben:
l = get_list()
return l[0] if l else None
Weitere Möglichkeiten demonstriert dies hier zu tun, mit Erklärungen
for
Wenn ich an kluge Weise zu denken begann versuchen, dies zu tun, ist dies das zweite, was ich dachte:
for item in get_list():
return item
Dies setzt die Funktion hier endet, implizit Rückkehr None
wenn get_list
eine leere Liste zurückgibt. Der unter expliziter Code entspricht genau:
for item in get_list():
return item
return None
if some_list
Im Folgenden wurde auch vorgeschlagen (ich die falschen Variablennamen korrigiert), die auch den impliziten None
verwendet. Dies würde die obige vorzuziehen sein, da es die logische Überprüfung statt einer Iteration verwendet, die nicht passieren können. Dies sollte einfacher sein, sofort zu verstehen, was geschieht. Aber wenn wir aus Gründen der Lesbarkeit und Wartbarkeit zu schreiben, sollten wir auch den expliziten return None
am Ende hinzufügen:
some_list = get_list()
if some_list:
return some_list[0]
Slice or [None]
und wählen nullten Index
Dies ist auch in der meist up-gestimmt Antwort:
return (get_list()[:1] or [None])[0]
Die Scheibe ist nicht erforderlich, und schafft eine Extra-Posten-Liste im Speicher. Folgendes sollte mehr performant sein. Um zu erklären, or
das zweite Element zurückkehrt, wenn die erste False
in einem booleschen Kontext ist, also wenn get_list
eine leere Liste zurückgibt, enthielt der Ausdruck in den Klammern eine Liste mit ‚Keine‘ zurück, die dann durch den 0
Index zugegriffen werden :
return (get_list() or [None])[0]
Die nächste nutzt die Tatsache, dass und gibt den zweiten Punkt, wenn die ersten True
in einem Booleschen Kontext ist, und da es my_list zweimal verweist, es ist nicht besser als der ternäre Ausdruck (und technisch keinen Einzeiler):
my_list = get_list()
return (my_list and my_list[0]) or None
next
Dann haben wir die folgende geschickte Nutzung des eingebauten next
und iter
return next(iter(get_list()), None)
Um zu erklären, iter
gibt einen Iterator mit einem .next
Verfahren. (.__next__
in Python 3.) Dann wird die builtin next
nennt diese .next
Methode, und wenn der Iterator erschöpft ist, gibt den Standard wir geben, None
.
redundante ternären Ausdruck (a if b else c
) und kreist zurück
Die unten wurde vorgeschlagen, aber die Umkehrung wäre vorzuziehen, wie die Logik in der Regel besser in den positiven anstelle der negativen verstanden wird. Da get_list
zweimal aufgerufen wird, es sei denn, das Ergebnis in irgendeiner Weise memoized wird, wäre dies eine schlechte Leistung:
return None if not get_list() else get_list()[0]
Je besser invers:
return get_list()[0] if get_list() else None
Noch besser ist, eine lokale Variable verwenden, so dass get_list
nur einmal aufgerufen wird, und Sie haben die empfohlene Pythonic Lösung zuerst diskutiert:
l = get_list()
return l[0] if l else None
In Bezug auf Idiome gibt es eine itertools Rezept genannt nth
.
Von itertools Rezepte:
def nth(iterable, n, default=None):
"Returns the nth item or a default value"
return next(islice(iterable, n, None), default)
Wenn Sie Einzeiler wollen, sollten Sie eine Bibliothek installieren, die dieses Rezept für Sie implementiert, z.B. more_itertools
:
import more_itertools as mit
mit.nth([3, 2, 1], 0)
# 3
mit.nth([], 0) # default is `None`
# None
Ein weiteres Werkzeug zur Verfügung, das nur das erste Element zurückgibt, die so genannte more_itertools.first
.
mit.first([3, 2, 1])
# 3
mit.first([], default=None)
# None
Diese itertools maßstab allgemein für jede iterable, nicht nur für Listen.
Aus Neugier, lief ich Timings auf zwei der Lösungen. Die Lösung, die eine return-Anweisung verwendet eine for-Schleife vorzeitig zu beenden ist etwas teurer auf meinem Rechner mit Python 2.5.1, ich vermuten, dass dies mit dem Einrichten des iterable zu tun hat.
import random
import timeit
def index_first_item(some_list):
if some_list:
return some_list[0]
def return_first_item(some_list):
for item in some_list:
return item
empty_lists = []
for i in range(10000):
empty_lists.append([])
assert empty_lists[0] is not empty_lists[1]
full_lists = []
for i in range(10000):
full_lists.append(list([random.random() for i in range(10)]))
mixed_lists = empty_lists[:50000] + full_lists[:50000]
random.shuffle(mixed_lists)
if __name__ == '__main__':
ENV = 'import firstitem'
test_data = ('empty_lists', 'full_lists', 'mixed_lists')
funcs = ('index_first_item', 'return_first_item')
for data in test_data:
print "%s:" % data
for func in funcs:
t = timeit.Timer('firstitem.%s(firstitem.%s)' % (
func, data), ENV)
times = t.repeat()
avg_time = sum(times) / len(times)
print " %s:" % func
for time in times:
print " %f seconds" % time
print " %f seconds avg." % avg_time
Dies sind die Zeiten ich habe:
empty_lists: index_first_item: 0.748353 seconds 0.741086 seconds 0.741191 seconds 0.743543 seconds avg. return_first_item: 0.785511 seconds 0.822178 seconds 0.782846 seconds 0.796845 seconds avg. full_lists: index_first_item: 0.762618 seconds 0.788040 seconds 0.786849 seconds 0.779169 seconds avg. return_first_item: 0.802735 seconds 0.878706 seconds 0.808781 seconds 0.830074 seconds avg. mixed_lists: index_first_item: 0.791129 seconds 0.743526 seconds 0.744441 seconds 0.759699 seconds avg. return_first_item: 0.784801 seconds 0.785146 seconds 0.840193 seconds 0.803380 seconds avg.
try:
return a[0]
except IndexError:
return None
def head(iterable):
try:
return iter(iterable).next()
except StopIteration:
return None
print head(xrange(42, 1000) # 42
print head([]) # None
BTW: Ich würde Ihren allgemeinen Programmablauf in etwa wie folgt überarbeiten:
lists = [
["first", "list"],
["second", "list"],
["third", "list"]
]
def do_something(element):
if not element:
return
else:
# do something
pass
for li in lists:
do_something(head(li))
(Vermeidung von Wiederholungen, wann immer möglich)
Wie wäre es damit:
(my_list and my_list[0]) or None
Hinweis: Dies sollte für Listen von Objekten funktionieren, aber es könnte unter den Kommentaren per falsche Antwort bei Zahlen- oder Zeichenfolgenliste zurück
.könnten Sie Methode extrahieren . Mit anderen Worten extrahieren, dass Code in eine Methode, die Sie dann nennen würde.
Ich würde nicht versuchen, es zu komprimieren viel mehr, die Einzeiler härter scheinen als die ausführliche Version zu lesen. Und wenn Sie Methode extrahieren verwenden, es ist ein Einzeiler;)
Mit dem und-oder Trick:
a = get_list()
return a and a[0] or None
Und was ist: next(iter(get_list()), None)
?
Könnte nicht die schnellsten hier sein, aber ist Standard (ab Python 2.6) und prägnant.
Wahrscheinlich nicht die schnellste Lösung, aber niemand erwähnte diese Option:
dict(enumerate(get_list())).get(0)
Wenn get_list()
kann None
zurückkehren können Sie:
dict(enumerate(get_list() or [])).get(0)
Vorteile:
-on Zeile
-Sie einfach anrufen get_list()
einmal
-einfach verstehen
Mein Anwendungsfall war nur den Wert einer lokalen Variablen zu setzen.
Ich persönlich fand der Versuch und außer Stil sauberer lesen
items = [10, 20]
try: first_item = items[0]
except IndexError: first_item = None
print first_item
als eine Liste schneiden.
items = [10, 20]
first_item = (items[:1] or [None, ])[0]
print first_item
if mylist != []:
print(mylist[0])
else:
print(None)
Einige Leute haben so etwas wie dies vorschlagen tun:
list = get_list()
return list and list[0] or None
Das ist in vielen Fällen funktioniert, aber es funktioniert nur, wenn die Liste [0] nicht gleich 0, False oder eine leere Zeichenfolge. Wenn die Liste [0] 0 ist, False oder eine leere Zeichenfolge, wird die Methode falsch None zurück.
ich diesen Fehler in meinem eigenen Code erstellt habe einmal zu oft!
ist nicht die idiomatische Python entspricht C-Stil ternäre Operatoren
cond and true_expr or false_expr
dh.
list = get_list()
return list and list[0] or None