Zum Beispiel, wenn Sie das folgende übergeben:
a = []
Wie kann ich überprüfen, ob a
leer ist?
Zum Beispiel, wenn Sie das folgende übergeben:
a = []
Wie kann ich überprüfen, ob a
leer ist?
if not a:
print("List is empty")
Die Verwendung des implizite Booleaness der leeren list
ist ziemlich pythonisch.
Ich spiele des Teufels Advokat. Ich verstehe nicht, warum diese Redewendung als pythonisch angesehen wird. 'Explizit ist besser als implizit', richtig? Diese Prüfung scheint nicht sehr explizit zu sein, was geprüft wird.
@JamesMcMahon - es ist ein Kompromiss zwischen Explizitheit und Typflexibilität. im Allgemeinen bedeutet "explizit sein", keine "magischen" Dinge zu tun. andererseits bedeutet "duck typing", mit allgemeineren Schnittstellen zu arbeiten, anstatt explizit nach Typen zu suchen. also etwas wie if a == []
einen bestimmten Typ erzwingt ( () == []
es False
). Hier scheint der allgemeine Konsens zu sein, dass das Tippen von Enten den Sieg davonträgt (was in der Tat bedeutet, dass __nonzero__
ist die Schnittstelle zur Prüfung der Leere docs.python.org/reference/datamodel.html#object.__nonzero__ )
Diese Methode funktioniert nicht auf Numpy-Arrays.. so ich denke, wenn len(a) == 0 ist vorzuziehen, sowohl in Bezug auf die "Ente Typisierung" und implicitness.
Der pythonische Weg, dies zu tun, führt über die PEP 8 Styleguide .
Für Sequenzen (Zeichenketten, Listen, Tupel) wird die Tatsache verwendet, dass leere Sequenzen falsch sind:
# Correct: if not seq: if seq: # Wrong: if len(seq): if not len(seq):
Der zweite Weg scheint besser, wenn Sie signalisieren wollen, dass seq
soll eine Art listenartiges Objekt sein.
@BallpointBen, die nach Ansicht der Befürworter des Pythonismus so weit wie möglich implizit in der Benennung der Variablen enthalten sein sollte
@BallpointBen versuchen Sie es mit Pythons Typenhinweis um zu signalisieren, was eine Variable sein soll. Sie wurde in Version 3.5 eingeführt.
Zu prüfen, ob die Länge einer Liste gleich Null ist, anstatt nur zu prüfen, ob die Liste falsch ist, ist hässlich und unpythonisch. Jeder, der mit Python vertraut ist, wird nicht denken li
überhaupt ein bool ist, und kümmert sich nicht darum. Wenn es wichtig ist, sollten Sie einen Kommentar hinzufügen, nicht mehr Code.
Dies scheint ein unnötig genauer Test zu sein, der oft langsamer ist und IMHO immer weniger lesbar ist. Anstatt die Größe von etwas Leerem zu prüfen, warum nicht einfach prüfen, ob es leer ist?
Wie auch immer, der Grund dafür, dass dies schlecht ist (und dass das Verletzen von Idiomen in einer Sprache mit starken Idiomen wie Python im Allgemeinen schlecht ist), ist, dass es dem Leser signalisiert, dass Sie speziell die Länge aus irgendeinem Grund überprüfen (z.B. weil Sie wollen None
o 0
eine Ausnahme auszulösen, statt zu bestehen). Wenn Sie das also ohne Grund tun, ist das irreführend - und es bedeutet auch, dass Ihr Code tut Die Unterscheidung ist unsichtbar, weil Sie den Rest der Quelle mit "Wolfsgeheul" überzogen haben.
Dies ist der erste Google-Treffer für "Python Test leeres Array" und ähnliche Abfragen, plus andere Leute scheinen die Frage über nur Listen zu verallgemeinern, so dass ich dachte, ich würde eine Warnung für eine andere Art von Sequenz hinzufügen, die eine Menge Leute verwenden könnten.
Sie müssen mit NumPy-Arrays vorsichtig sein, weil andere Methoden, die gut funktionieren für list
s oder andere Standardcontainer für NumPy-Arrays nicht funktionieren. Ich erkläre unten warum, aber kurz gesagt, die bevorzugte Methode ist zu verwenden size
.
Der "pythonische" Weg schlägt bei NumPy-Arrays fehl, weil NumPy versucht, das Array in ein Array von bool
s, und if x
versucht, alle diese Faktoren zu bewerten bool
s auf einmal für eine Art aggregierten Wahrheitswert. Aber das macht keinen Sinn, also bekommt man eine ValueError
:
>>> x = numpy.array([0,1])
>>> if x: print("x")
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Aber zumindest der obige Fall zeigt, dass es nicht geklappt hat. Wenn Sie zufällig ein NumPy-Array mit genau einem Element haben, wird die if
Anweisung "funktioniert" in dem Sinne, dass Sie keine Fehlermeldung erhalten. Wenn dieses eine Element jedoch zufällig 0
(ou 0.0
ou False
, ...), die if
Anweisung führt fälschlicherweise zu False
:
>>> x = numpy.array([0,])
>>> if x: print("x")
... else: print("No x")
No x
Aber klar x
existiert und nicht leer ist! Dieses Ergebnis ist nicht das, was Sie wollten.
len
kann zu unerwarteten Ergebnissen führenZum Beispiel,
len( numpy.zeros((1,0)) )
gibt 1 zurück, auch wenn das Array keine Elemente hat.
Wie in der SciPy FAQ ist die korrekte Methode in allen Fällen, in denen Sie wissen, dass Sie ein NumPy-Array haben, die Verwendung von if x.size
:
>>> x = numpy.array([0,1])
>>> if x.size: print("x")
x
>>> x = numpy.array([0,])
>>> if x.size: print("x")
... else: print("No x")
x
>>> x = numpy.zeros((1,0))
>>> if x.size: print("x")
... else: print("No x")
No x
Wenn Sie nicht sicher sind, ob es sich um eine list
, ein NumPy-Array oder etwas anderes, können Sie diesen Ansatz mit die Antwort von @dubiousjim um sicherzustellen, dass für jeden Typ der richtige Test verwendet wird. Nicht sehr "pythonisch", aber es stellt sich heraus, dass NumPy die Pythonizität zumindest in diesem Sinne absichtlich gebrochen hat.
Wenn Sie mehr tun müssen, als nur zu prüfen, ob die Eingabe leer ist, und Sie andere NumPy-Funktionen wie Indexierung oder mathematische Operationen verwenden, ist es wahrscheinlich effizienter (und sicherlich häufiger), die Eingabe zu erzwingen zu sein ein NumPy-Array. Es gibt ein paar nette Funktionen, um dies schnell zu tun - am wichtigsten sind numpy.asarray
. Dies nimmt Ihre Eingabe, tut nichts, wenn es bereits ein Array ist, oder wandelt Ihre Eingabe in ein Array um, wenn es eine Liste, ein Tupel, etc. ist, und konvertiert es optional in das von Ihnen gewählte dtype
. Es ist also sehr schnell, wann immer es möglich ist, und es stellt sicher, dass Sie einfach davon ausgehen können, dass die Eingabe ein NumPy-Array ist. Normalerweise verwenden wir sogar einfach den gleichen Namen, da die Konvertierung in ein Array nicht außerhalb des aktuellen Umfang :
x = numpy.asarray(x, dtype=numpy.double)
Dadurch wird die x.size
Check funktionieren in allen Fällen, die ich auf dieser Seite sehe.
Es ist erwähnenswert, dass dies kein Fehler in Python ist, sondern eher ein absichtlicher Vertragsbruch durch numpy
- numpy
ist eine Bibliothek mit einem sehr spezifischen Anwendungsfall, und sie hat eine andere "natürliche" Definition dessen, was Wahrhaftigkeit auf einem Array ist, als der Python-Standard für Container. Es macht Sinn, für diesen Fall zu optimieren, in der Art und Weise, wie pathlib
utilise /
zur Verkettung von Pfaden anstelle von +
- Es ist nicht standardisiert, macht aber in diesem Zusammenhang Sinn.
Einverstanden. Ich möchte nur darauf hinweisen, dass es wichtig ist, sich daran zu erinnern, dass numpy die Entscheidung getroffen hat, die Duck-Typisierung sowohl für die sehr häufigen if x
y len(x)
Idiome - und manchmal kann dieser Bruch sehr schwer zu erkennen und zu beheben sein.
Ich weiß nicht, für mich, wenn eine Methode namens len(x) nicht die Array-Länge zurückgibt, weil Annahmen, es der Name ist schlecht konzipiert.
Beste Möglichkeit zu prüfen, ob eine Liste leer ist
Zum Beispiel, wenn Sie das folgende übergeben:
a = []
Wie kann ich prüfen, ob a leer ist?
Stellen Sie die Liste in einen booleschen Kontext (zum Beispiel mit einem if
o while
Aussage). Es wird getestet False
wenn sie leer ist, und True
anders. Zum Beispiel:
if not a: # do this!
print('a is an empty list')
PEP 8 , die offizielle Python-Stilanleitung für Python-Code in der Python-Standardbibliothek, behauptet:
Für Sequenzen (Zeichenketten, Listen, Tupel) wird die Tatsache verwendet, dass leere Sequenzen falsch sind.
Yes: if not seq: if seq: No: if len(seq): if not len(seq):
Wir sollten erwarten, dass der Code der Standardbibliothek so leistungsfähig und korrekt wie möglich ist. Aber warum ist das der Fall, und warum brauchen wir diese Anleitung?
Ich sehe häufig Code wie diesen von erfahrenen Programmierern, die neu in Python sind:
if len(a) == 0: # Don't do this!
print('a is an empty list')
Und Benutzer von "Lazy Languages" könnten versucht sein, dies zu tun:
if a == []: # Don't do this!
print('a is an empty list')
Diese sind in ihren jeweiligen anderen Sprachen korrekt. Und in Python ist dies sogar semantisch korrekt.
Wir halten dies jedoch für unpythonisch, da Python diese Semantik direkt in der Schnittstelle des Listenobjekts durch boolesche Koerziation unterstützt.
Von der docs (und beachten Sie insbesondere die Einbeziehung der leeren Liste, []
):
Standardmäßig gilt ein Objekt als wahr, es sei denn, seine Klasse definiert entweder eine
__bool__()
Methode, die Folgendes zurückgibtFalse
ou un__len__()
Methode die Null zurückgibt, wenn sie mit dem Objekt aufgerufen wird. Hier sind die meisten der eingebauten Objekte, die als falsch gelten:
- Konstanten, die als falsch definiert sind:
None
yFalse
.- Null eines beliebigen numerischen Typs:
0
,0.0
,0j
,Decimal(0)
,Fraction(0, 1)
- leere Sequenzen und Sammlungen:
''
,()
,[]
,{}
,set()
,range(0)
Und die Dokumentation des Datenmodells:
Aufgerufen zur Implementierung der Wahrheitswertprüfung und der eingebauten Operation
bool()
; sollte zurückkehrenFalse
oTrue
. Wenn diese Methode nicht definiert ist,__len__()
wird aufgerufen, wenn es definiert ist, und das Objekt gilt als wahr, wenn sein Ergebnis ungleich Null ist. Wenn eine Klasse weder__len__()
noch__bool__()
werden alle seine Instanzen als wahr angesehen.
y
Aufgerufen zur Implementierung der eingebauten Funktion
len()
. sollte die Länge des Objekts zurückgeben, eine ganze Zahl >= 0. Außerdem kann ein Objekt, das keine__bool__()
Methode und deren__len__()
Methode Null zurückgibt, wird in einem booleschen Kontext als falsch betrachtet.
Also stattdessen dies:
if len(a) == 0: # Don't do this!
print('a is an empty list')
oder dies:
if a == []: # Don't do this!
print('a is an empty list')
Tun Sie dies:
if not a:
print('a is an empty list')
Lohnt sich das? (Beachten Sie, dass weniger Zeit für die Durchführung einer gleichwertigen Operation besser ist).
>>> import timeit
>>> min(timeit.repeat(lambda: len([]) == 0, repeat=100))
0.13775854044661884
>>> min(timeit.repeat(lambda: [] == [], repeat=100))
0.0984637276455409
>>> min(timeit.repeat(lambda: not [], repeat=100))
0.07878462291455435
Zur Veranschaulichung hier die Kosten für den Aufruf der Funktion und die Erstellung und Rückgabe einer leeren Liste, die Sie von den Kosten der oben verwendeten Leerheitsprüfungen abziehen könnten:
>>> min(timeit.repeat(lambda: [], repeat=100))
0.07074015751817342
Wir sehen, dass entweder Prüfung auf Länge mit der eingebauten Funktion len
im Vergleich zu 0
o die Prüfung gegen eine leere Liste ist viel weniger leistungsfähig als die Verwendung der eingebauten Syntax der Sprache, wie sie dokumentiert ist.
Und warum?
Für die len(a) == 0
prüfen:
Zuerst muss Python die Globals überprüfen, um festzustellen, ob len
beschattet wird.
Dann muss er die Funktion aufrufen, laden 0
und führen den Gleichheitsvergleich in Python (statt in C) durch:
>>> import dis
>>> dis.dis(lambda: len([]) == 0)
1 0 LOAD_GLOBAL 0 (len)
2 BUILD_LIST 0
4 CALL_FUNCTION 1
6 LOAD_CONST 1 (0)
8 COMPARE_OP 2 (==)
10 RETURN_VALUE
Und für die [] == []
er muss eine unnötige Liste erstellen und dann wiederum die Vergleichsoperation in der virtuellen Maschine von Python durchführen (im Gegensatz zu C)
>>> dis.dis(lambda: [] == [])
1 0 BUILD_LIST 0
2 BUILD_LIST 0
4 COMPARE_OP 2 (==)
6 RETURN_VALUE
Die "Pythonic"-Methode ist eine viel einfachere und schnellere Prüfung, da die Länge der Liste im Header der Objektinstanz zwischengespeichert wird:
>>> dis.dis(lambda: not [])
1 0 BUILD_LIST 0
2 UNARY_NOT
4 RETURN_VALUE
Dies ist eine Erweiterung von
PyObject
das dieob_size
Bereich. Dies wird nur für Objekte verwendet, die eine gewisse Länge haben. Dieser Typ kommt in der Python/C-API nicht oft vor. Er entspricht den Feldern, die durch die Erweiterung derPyObject_VAR_HEAD
Makro.
Von der Quelle c in Include/listobject.h :
typedef struct {
PyObject_VAR_HEAD
/* Vector of pointers to list elements. list[0] is ob_item[0], etc. */
PyObject **ob_item;
/* ob_item contains space for 'allocated' elements. The number
* currently in use is ob_size.
* Invariants:
* 0 <= ob_size <= allocated
* len(list) == ob_size
Ich möchte darauf hinweisen, dass dies auch für den nicht leeren Fall gilt, obwohl es ziemlich hässlich ist wie bei
l=[]
puis%timeit len(l) != 0
90,6 ns ± 8,3 ns,%timeit l != []
55,6 ns ± 3,09,%timeit not not l
38,5 ns ± 0,372. Aber es gibt keine Möglichkeit, dass jemand dienot not l
trotz der dreifachen Geschwindigkeit. Das sieht lächerlich aus. Aber die Geschwindigkeit siegt
Ich nehme an, das Problem ist das Testen mit timeit, da geradeif l:
ist ausreichend, aber überraschenderweise%timeit bool(l)
ergibt 101 ns ± 2,64 ns. Interessanterweise gibt es keine Möglichkeit, ohne diesen Nachteil auf bool zu zwingen.%timeit l
ist nutzlos, da keine Umwandlung stattfinden würde.
IPython-Magie, %timeit
ist hier nicht völlig nutzlos:
In [1]: l = []
In [2]: %timeit l
20 ns ± 0.155 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
In [3]: %timeit not l
24.4 ns ± 1.58 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [4]: %timeit not not l
30.1 ns ± 2.16 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
Wir können sehen, dass die Kosten für jede zusätzliche Einheit ein wenig linear sind. not
hier. Wir wollen die Kosten sehen, ceteris paribus d.h. alles andere ist gleich - wobei alles andere so weit wie möglich minimiert wird:
In [5]: %timeit if l: pass
22.6 ns ± 0.963 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [6]: %timeit if not l: pass
24.4 ns ± 0.796 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [7]: %timeit if not not l: pass
23.4 ns ± 0.793 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
Betrachten wir nun den Fall einer nicht leeren Liste:
In [8]: l = [1]
In [9]: %timeit if l: pass
23.7 ns ± 1.06 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [10]: %timeit if not l: pass
23.6 ns ± 1.64 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [11]: %timeit if not not l: pass
26.3 ns ± 1 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
Wir sehen hier, dass es kaum einen Unterschied macht, ob Sie eine tatsächliche bool
auf die Bedingungsprüfung oder die Liste selbst, und wenn überhaupt, ist es schneller, die Liste so zu übergeben, wie sie ist.
Python ist in C geschrieben; es verwendet seine Logik auf der C-Ebene. Alles, was Sie in Python schreiben, wird langsamer sein. Und es wird wahrscheinlich um Größenordnungen langsamer sein, es sei denn, Sie verwenden direkt die in Python eingebauten Mechanismen.
Ich möchte darauf hinweisen, dass dies auch für den nicht leeren Fall gilt, obwohl es ziemlich hässlich ist wie bei l=[]
puis %timeit len(l) != 0
90,6 ns ± 8,3 ns, %timeit l != []
55,6 ns ± 3,09, %timeit not not l
38,5 ns ± 0,372. Aber es gibt keine Möglichkeit, dass jemand die not not l
trotz der dreifachen Geschwindigkeit. Das sieht lächerlich aus. Aber die Geschwindigkeit siegt
Ich nehme an, das Problem ist das Testen mit timeit, da gerade if l:
ist ausreichend, aber überraschenderweise %timeit bool(l)
ergibt 101 ns ± 2,64 ns. Interessanterweise gibt es keine Möglichkeit, ohne diesen Nachteil auf bool zu zwingen. %timeit l
ist nutzlos, da keine Umwandlung stattfinden würde.
Die beste Antwort bisher, danke! Aufzeigen der wahren Logik von Python mit magicmethods und "Python ist in C geschrieben; es verwendet seine Logik auf C-Ebene. Alles, was Sie in Python schreiben, wird langsamer sein. Und es wird wahrscheinlich um Größenordnungen langsamer sein" ist der Schlüssel. Andernfalls verfällt man in "Vorlieben" und kommt nie zu einer richtigen Schlussfolgerung.
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.