567 Stimmen

Prüfen, ob alle Elemente in einer Liste identisch sind

Ich brauche eine Funktion, die eine list und Ausgänge True wenn alle Elemente der Eingabeliste mit dem Standard-Gleichheitsoperator als gleich bewertet werden und False sonst.

Ich denke, es wäre am besten, durch die Liste zu iterieren und benachbarte Elemente zu vergleichen und dann AND alle daraus resultierenden booleschen Werte. Aber ich bin mir nicht sicher, wie man das am besten mit Python machen kann.

619voto

kennytm Punkte 488916

使用方法 itertools.groupby 参照 die itertools Rezepte ):

from itertools import groupby

def all_equal(iterable):
    g = groupby(iterable)
    return next(g, True) and not next(g, False)

oder ohne groupby :

def all_equal(iterator):
    iterator = iter(iterator)
    try:
        first = next(iterator)
    except StopIteration:
        return True
    return all(first == x for x in iterator)

Es gibt eine Reihe von alternativen Einzeilern, die Sie in Betracht ziehen könnten:

  1. Umwandlung der Eingabe in eine Menge und Überprüfung, ob sie nur ein oder null (falls die Eingabe leer ist) Elemente enthält

    def all_equal2(iterator):
        return len(set(iterator)) <= 1
  2. Vergleich mit der Eingabeliste ohne den ersten Eintrag

    def all_equal3(lst):
        return lst[:-1] == lst[1:]
  3. Zählen, wie oft der erste Eintrag in der Liste erscheint

    def all_equal_ivo(lst):
        return not lst or lst.count(lst[0]) == len(lst)
  4. Vergleich mit einer Liste, in der das erste Element wiederholt wird

    def all_equal_6502(lst):
        return not lst or [lst[0]]*len(lst) == lst

Aber sie haben auch einige Nachteile, nämlich:

  1. all_equal y all_equal2 können beliebige Iteratoren verwenden, aber die anderen müssen eine Sequenzeingabe annehmen, typischerweise konkrete Container wie eine Liste oder ein Tupel.
  2. all_equal y all_equal3 stoppen, sobald ein Unterschied festgestellt wird (was als " Kurzschluss "), wohingegen alle Alternativen eine Iteration über die gesamte Liste erfordern, selbst wenn man weiß, dass die Antwort lautet False nur anhand der ersten beiden Elemente.
  3. all_equal2 der Inhalt muss sein Hashable . Eine Liste von Listen löst eine TypeError zum Beispiel.
  4. all_equal2 (im schlimmsten Fall) und all_equal_6502 eine Kopie der Liste erstellen, was bedeutet, dass Sie den doppelten Speicherplatz benötigen.

Unter Python 3.9 wird mit perfplot erhalten wir diese Zeitangaben (niedriger Runtime [s] ist besser):

for a list with a difference in the first two elements, groupby is fastestfor a list with no differences, count(l 0 ) is fastest

393voto

Ivo van der Wijk Punkte 15553

Eine Lösung, die schneller ist als die Verwendung von set() und mit Sequenzen (nicht Iterablen) funktioniert, besteht darin, einfach das erste Element zu zählen. Dies setzt voraus, dass die Liste nicht leer ist (aber das ist trivial zu überprüfen, und Sie können selbst entscheiden, wie das Ergebnis bei einer leeren Liste aussehen soll)

x.count(x[0]) == len(x)

einige einfache Benchmarks:

>>> timeit.timeit('len(set(s1))<=1', 's1=[1]*5000', number=10000)
1.4383411407470703
>>> timeit.timeit('len(set(s1))<=1', 's1=[1]*4999+[2]', number=10000)
1.4765670299530029
>>> timeit.timeit('s1.count(s1[0])==len(s1)', 's1=[1]*5000', number=10000)
0.26274609565734863
>>> timeit.timeit('s1.count(s1[0])==len(s1)', 's1=[1]*4999+[2]', number=10000)
0.25654196739196777

239voto

ninjagecko Punkte 82995

[edit: Diese Antwort richtet sich an die derzeit meistgewählte itertools.groupby (was eine gute Antwort ist) später beantworten].

Ohne das Programm neu zu schreiben, ist die asymptotisch leistungsfähigste y am besten lesbar ist, lautet wie folgt:

all(x==myList[0] for x in myList)

(Ja, das funktioniert sogar mit einer leeren Liste! Das liegt daran, dass dies einer der wenigen Fälle ist, in denen Python eine faule Semantik hat.)

Dies wird zum frühestmöglichen Zeitpunkt fehlschlagen, so dass es asymptotisch optimal ist (die erwartete Zeit ist ungefähr O(#uniques) statt O(N), aber die Zeit im schlimmsten Fall immer noch O(N)). Dies setzt voraus, dass Sie die Daten vorher noch nicht gesehen haben...

(Wenn Ihnen die Leistung wichtig ist, Sie aber nicht so viel Wert auf die Leistung legen, können Sie zuerst die üblichen Standardoptimierungen durchführen, wie z.B. das Anheben des myList[0] Konstante aus der Schleife zu entfernen und klobige Logik für den Randfall hinzuzufügen, obwohl dies etwas ist, was der Python-Compiler irgendwann lernen könnte, wie man es macht, und daher sollte man es nicht tun, wenn es nicht absolut notwendig ist, da es die Lesbarkeit für minimalen Gewinn zerstört).

Wenn Sie etwas mehr Wert auf Leistung legen, ist dies doppelt so schnell wie oben, aber etwas ausführlicher:

def allEqual(iterable):
    iterator = iter(iterable)

    try:
        firstItem = next(iterator)
    except StopIteration:
        return True

    for x in iterator:
        if x!=firstItem:
            return False
    return True

Wenn Sie noch mehr Wert auf Leistung legen (aber nicht genug, um Ihr Programm neu zu schreiben), verwenden Sie die derzeit meistgewählte itertools.groupby Antwort, die doppelt so schnell ist wie allEqual weil es sich wahrscheinlich um optimierten C-Code handelt. (Laut der Dokumentation sollte es (ähnlich wie bei dieser Antwort) keinen Speicher-Overhead haben, da der Lazy-Generator nie in eine Liste ausgewertet wird... worüber man sich Sorgen machen könnte, aber der Pseudocode zeigt, dass die gruppierten "Listen" eigentlich Lazy-Generatoren sind).

Wenn Sie noch mehr Wert auf Leistung legen, lesen Sie weiter...


Nebenbemerkungen zur Leistung, da die anderen Antworten aus unerfindlichen Gründen davon sprechen:

... wenn Sie die Daten schon einmal gesehen haben und wahrscheinlich eine Art von Sammeldatenstruktur verwenden und Ihnen die Leistung wirklich wichtig ist, können Sie .isAllEqual() umsonst O(1), indem Sie Ihre Struktur mit einer Counter die bei jeder Einfügung/Löschung/etc. aktualisiert wird, und prüft nur, ob sie die Form {something:someCount} d.h. len(counter.keys())==1 Alternativ können Sie auch einen Zähler in einer separaten Variablen aufbewahren. Dies ist nachweislich besser als alles andere bis zu einem konstanten Faktor. Vielleicht können Sie auch das FFI von Python mit ctypes mit der von Ihnen gewählten Methode und vielleicht mit einer Heuristik (z. B. wenn es sich um eine Sequenz mit getitem , dann Prüfung des ersten Elements, des letzten Elements, dann der Elemente in der Reihenfolge).

Natürlich hat die Lesbarkeit auch etwas für sich.

75voto

cbalawat Punkte 1043

Wandeln Sie Ihre Eingabe in eine set :

len(set(the_list)) <= 1

Verwendung von set entfernt alle doppelten Elemente. <= 1 ist, so dass es korrekt zurückgibt True wenn die Eingabe leer ist.

Dies setzt voraus, dass alle Elemente in Ihrer Eingabe Hashable . Sie erhalten eine TypeError wenn Sie zum Beispiel eine Liste von Listen übergeben.

49voto

codaddict Punkte 426877

Sie können die Liste in ein Set umwandeln. Eine Menge kann keine Duplikate enthalten. Wenn also alle Elemente in der ursprünglichen Liste identisch sind, hat die Menge nur ein Element.

if len(set(input_list)) == 1:
    # input_list has all identical elements.

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