In Python habe ich eine Liste, die nur einen Wahrheitswert haben sollte (d bool(value) is True
. H. ). Gibt es eine clevere Möglichkeit, dies zu überprüfen? Im Moment iteriere ich nur durch die Liste und überprüfe manuell:
def only1(l)
true_found = False
for v in l:
if v and not true_found:
true_found=True
elif v and true_found:
return False #"Too Many Trues"
return true_found
Dies scheint unelegant und nicht sehr pythonisch. Gibt es eine klügere Möglichkeit, dies zu tun?
(= 1 (count-if #'identity list))
.sum(lst) == 1
True
Möchten Sie überprüfen, ob es nur einen oder nur einen Wahrheitswert gibt?Antworten:
Die ausführlichste Lösung ist nicht immer die uneleganteste Lösung. Daher füge ich nur eine geringfügige Änderung hinzu (um einige redundante boolesche Auswertungen zu speichern):
def only1(l): true_found = False for v in l: if v: # a True was found! if true_found: # found too many True's return False else: # found the first True true_found = True # found zero or one True value return true_found
Hier sind einige Zeitpunkte zum Vergleich:
# file: test.py from itertools import ifilter, islice def OP(l): true_found = False for v in l: if v and not true_found: true_found=True elif v and true_found: return False #"Too Many Trues" return true_found def DavidRobinson(l): return l.count(True) == 1 def FJ(l): return len(list(islice(ifilter(None, l), 2))) == 1 def JonClements(iterable): i = iter(iterable) return any(i) and not any(i) def moooeeeep(l): true_found = False for v in l: if v: if true_found: # found too many True's return False else: # found the first True true_found = True # found zero or one True value return true_found
Meine Ausgabe:
$ python -mtimeit -s 'import test; l=[True]*100000' 'test.OP(l)' 1000000 loops, best of 3: 0.523 usec per loop $ python -mtimeit -s 'import test; l=[True]*100000' 'test.DavidRobinson(l)' 1000 loops, best of 3: 516 usec per loop $ python -mtimeit -s 'import test; l=[True]*100000' 'test.FJ(l)' 100000 loops, best of 3: 2.31 usec per loop $ python -mtimeit -s 'import test; l=[True]*100000' 'test.JonClements(l)' 1000000 loops, best of 3: 0.446 usec per loop $ python -mtimeit -s 'import test; l=[True]*100000' 'test.moooeeeep(l)' 1000000 loops, best of 3: 0.449 usec per loop
Wie zu sehen ist, ist die OP-Lösung erheblich besser als die meisten anderen hier veröffentlichten Lösungen. Wie erwartet sind die besten diejenigen mit Kurzschlussverhalten, insbesondere die von Jon Clements veröffentlichte Lösung. Zumindest für den Fall von zwei frühen
True
Werten in einer langen Liste.Hier das gleiche für überhaupt keinen
True
Wert:$ python -mtimeit -s 'import test; l=[False]*100000' 'test.OP(l)' 100 loops, best of 3: 4.26 msec per loop $ python -mtimeit -s 'import test; l=[False]*100000' 'test.DavidRobinson(l)' 100 loops, best of 3: 2.09 msec per loop $ python -mtimeit -s 'import test; l=[False]*100000' 'test.FJ(l)' 1000 loops, best of 3: 725 usec per loop $ python -mtimeit -s 'import test; l=[False]*100000' 'test.JonClements(l)' 1000 loops, best of 3: 617 usec per loop $ python -mtimeit -s 'import test; l=[False]*100000' 'test.moooeeeep(l)' 100 loops, best of 3: 1.85 msec per loop
Ich habe die statistische Signifikanz nicht überprüft, aber interessanterweise scheinen diesmal die von FJ vorgeschlagenen und insbesondere die von Jon Clements wieder deutlich überlegen zu sein.
quelle
0.446
das schnellste?any
in C implementiert istsum
sind tatsächlich schlechter als der einfache und direkte Code des OP.Eine, die keine Importe erfordert:
def single_true(iterable): i = iter(iterable) return any(i) and not any(i)
Alternativ vielleicht eine besser lesbare Version:
def single_true(iterable): iterator = iter(iterable) # consume from "i" until first true or it's exhausted has_true = any(iterator) # carry on consuming until another true value / exhausted has_another_true = any(iterator) # True if exactly one true found return has_true and not has_another_true
Dies:
i
es einen wahren Wert hatquelle
any
wird gemäß docs True zurückgeben, sobald ein nicht falscher Wert gefunden wird. Danach suchen wir erneut nach einem wahren Wert, und wenn er gefunden wird, behandeln wir ihn als Fehler ... Dies funktioniert also für leere Listen, Listen / andere Sequenzen und alle iterierbaren ...x and not x = False
ist nur richtig, wennx
referenziell transparent ist.any()
- es ist ein dokumentiertes Merkmal der Funktion und die garantierte Funktionalität jeder Implementierung, die der Python-Spezifikation entspricht.Es hängt davon ab, ob Sie nur nach dem Wert suchen
True
oder nach anderen Werten suchen, dieTrue
logisch ausgewertet werden (wie11
oder"hello"
). Wenn der erstere:def only1(l): return l.count(True) == 1
Wenn letzteres:
def only1(l): return sum(bool(e) for e in l) == 1
da dies sowohl das Zählen als auch die Konvertierung in einer einzigen Iteration durchführen würde, ohne eine neue Liste erstellen zu müssen.
quelle
list(map(bool, l)).count(True)
return sum(bool(e) for e in l) == 1
.bool
Unterklassenint
und True / False verhalten sich in Bezug auf Arithmetik wie 1/0.l
als Variablennamen zu verwenden (es sieht zu sehr nach1
hier aus), und ich würde umschreibensum(bool(e) for e in l)
alssum(1 for e in l if e)
Eine einzeilige Antwort, die das Kurzschlussverhalten beibehält:
from itertools import ifilter, islice def only1(l): return len(list(islice(ifilter(None, l), 2))) == 1
Dies ist für sehr große Iterables, die relativ früh zwei oder mehr wahre Werte haben, erheblich schneller als die anderen Alternativen.
ifilter(None, itr)
gibt eine iterable, die nur wahrheitsgemäße Elemente liefert (x
ist wahrhaftig, wennbool(x)
zurückgegeben wirdTrue
).islice(itr, 2)
gibt eine Iterable an, die nur die ersten beiden Elemente von ergibtitr
. Indem wir dies in eine Liste konvertieren und überprüfen, ob die Länge gleich eins ist, können wir überprüfen, ob genau ein wahrheitsgemäßes Element vorhanden ist, ohne dass zusätzliche Elemente überprüft werden müssen, nachdem wir zwei gefunden haben.Hier sind einige Zeitvergleiche:
Setup-Code:
In [1]: from itertools import islice, ifilter In [2]: def fj(l): return len(list(islice(ifilter(None, l), 2))) == 1 In [3]: def david(l): return sum(bool(e) for e in l) == 1
Kurzschlussverhalten zeigen:
In [4]: l = range(1000000) In [5]: %timeit fj(l) 1000000 loops, best of 3: 1.77 us per loop In [6]: %timeit david(l) 1 loops, best of 3: 194 ms per loop
Große Liste, in der kein Kurzschluss auftritt:
In [7]: l = [0] * 1000000 In [8]: %timeit fj(l) 100 loops, best of 3: 10.2 ms per loop In [9]: %timeit david(l) 1 loops, best of 3: 189 ms per loop
Kleine Liste:
In [10]: l = [0] In [11]: %timeit fj(l) 1000000 loops, best of 3: 1.77 us per loop In [12]: %timeit david(l) 1000000 loops, best of 3: 990 ns per loop
Der
sum()
Ansatz ist also für sehr kleine Listen schneller, aber wenn die Eingabeliste größer wird, ist meine Version schneller, selbst wenn ein Kurzschluss nicht möglich ist. Wenn bei einem großen Eingang ein Kurzschluss möglich ist, ist der Leistungsunterschied deutlich.quelle
timeit
Experimente für einen objektiven Leistungsvergleich mit der OP-Lösung durchführen.True
irgendwo "früh" zwei Werte hat , wird dies beendet, im Vergleich zu den anderen Antworten, die ihre Räder für immer drehen und versuchen, die Zählung zu erhalten.Ich wollte mir das Nekromanten-Abzeichen verdienen, also verallgemeinerte ich die ausgezeichnete Antwort von Jon Clements, wobei ich die Vorteile der Kurzschlusslogik und der schnellen Überprüfung von Prädikaten bei allen bewahrte.
Also hier ist:
N (wahr) = n
def n_trues(iterable, n=1): i = iter(iterable) return all(any(i) for j in range(n)) and not any(i)
N (wahr) <= n:
def up_to_n_trues(iterable, n=1): i = iter(iterable) all(any(i) for j in range(n)) return not any(i)
N (wahr)> = n:
def at_least_n_trues(iterable, n=1): i = iter(iterable) return all(any(i) for j in range(n))
m <= N (wahr) <= n
def m_to_n_trues(iterable, m=1, n=1): i = iter(iterable) assert m <= n return at_least_n_trues(i, m) and up_to_n_trues(i, n - m)
quelle
>>> l = [0, 0, 1, 0, 0] >>> has_one_true = len([ d for d in l if d ]) == 1 >>> has_one_true True
quelle
Du kannst tun:
x = [bool(i) for i in x] return x.count(True) == 1
Oder
x = map(bool, x) return x.count(True) == 1
Aufbauend auf der Methode von @ JoranBeasley:
sum(map(bool, x)) == 1
quelle
if sum([bool(x) for x in list]) == 1
(Angenommen, alle Ihre Werte sind boolesch.)
Dies wäre wahrscheinlich schneller, wenn man es nur summiert
sum(list) == 1
Dies kann jedoch je nach Datentyp in Ihrer Liste zu Problemen führen.
quelle
Wenn es nur eine gibt
True
, sollte die Länge desTrue
s eins sein:def only_1(l): return 1 == len(filter(None, l))
quelle
Dies scheint zu funktionieren und sollte in der Lage sein, alle iterierbaren Elemente zu verarbeiten, nicht nur
list
s. Es schließt nach Möglichkeit kurz, um die Effizienz zu maximieren. Funktioniert sowohl in Python 2 als auch in Python 3.def only1(iterable): for i, x in enumerate(iterable): # check each item in iterable if x: break # truthy value found else: return False # no truthy value found for x in iterable[i+1:]: # one was found, see if there are any more if x: return False # found another... return True # only a single truthy value found testcases = [ # [[iterable, expected result], ... ] [[ ], False], [[False, False, False, False], False], [[True, False, False, False], True], [[False, True, False, False], True], [[False, False, False, True], True], [[True, False, True, False], False], [[True, True, True, True], False], ] for i, testcase in enumerate(testcases): correct = only1(testcase[0]) == testcase[1] print('only1(testcase[{}]): {}{}'.format(i, only1(testcase[0]), '' if correct else ', error given '+str(testcase[0])))
Ausgabe:
quelle
iter(x for x in my_list if x)
und dann zu verwendennext
, vielleicht schöner als zu verwendenmap
undlist.index
map
und loszuwerdenlist.index
.@ JonClements` Lösung für höchstens N True-Werte erweitert :
# Extend any() to n true values def _NTrue(i, n=1): for x in xrange(n): if any(i): # False for empty continue else: return False return True def NTrue(iterable, n=1): i = iter(iterable) return any(i) and not _NTrue(i, n)
bearbeiten: bessere Version
def test(iterable, n=1): i = iter(iterable) return sum(any(i) for x in xrange(n+1)) <= n
edit2: umfassen zumindest stimmt m und höchstens n stimmt
def test(iterable, n=1, m=1): i = iter(iterable) return m <= sum(any(i) for x in xrange(n+1)) <= n
quelle
iterable.count(True) = 3
,NTrue(iterable, 1) = False
,NTrue(iterable, 2) = False
,NTrue(iterable, 3) = True
,NTrue(iterable, 4) = True
, ... Es erstreckt sich im Wesentlichen denand not any(i)
Teiland not any(i) and not any(i) and not...
all(any(i) for i in xrange(n)) and not any(i)
?any
s zu summieren .any(i) and not all(any(i) for x in xrange(n))
?True and not all(<n booleans>)
logisch nicht dasselbe wiecount(True) <= n
? Die Idee ist immer noch, den kleinstmöglichen Satz zu testen und bei der ersten Fehlerbedingung zu brechen.def only1(l) sum(map(lambda x: 1 if x else 0, l)) == 1
Erläuterung: Die
map
Funktion bildet eine Liste in einer anderen Liste, tunTrue => 1
undFalse => 0
. Wir haben jetzt eine Liste von Nullen und Einsen anstelle von Wahr oder Falsch. Jetzt addieren wir einfach diese Liste und wenn es 1 ist, gab es nur einen wahren Wert.quelle
Ist es das, wonach du suchst?
sum(l) == 1
quelle
Der Vollständigkeit halber und um die erweiterte Verwendung des Python-Kontrollflusses für die Schleifeniteration zu demonstrieren, kann die zusätzliche Abrechnung in der akzeptierten Antwort vermieden werden, was dies etwas beschleunigt.:
def one_bool_true(iterable): it = iter(iterable) for i in it: if i: break else: #no break, didn't find a true element return False for i in it: # continue consuming iterator where left off if i: return False return True # didn't find a second true.
Der oben beschriebene einfache Steuerungsfluss nutzt Pythons ausgefeilte Schleifenfunktion: die
else
. Die Semantik lautet: Wenn Sie die Iteration über den Iterator beenden, den Sie verbrauchen, ohnebreak
ihn zu verlassen, geben Sie denelse
Block ein.Hier ist die akzeptierte Antwort, die etwas mehr Buchhaltung verwendet.
def only1(l): true_found = False for v in l: if v: # a True was found! if true_found: # found too many True's return False else: # found the first True true_found = True # found zero or one True value return true_found
um diese zu messen:
import timeit >>> min(timeit.repeat(lambda: one_bool_true([0]*100 + [1, 1]))) 13.992251592921093 >>> min(timeit.repeat(lambda: one_bool_true([1, 1] + [0]*100))) 2.208037032979064 >>> min(timeit.repeat(lambda: only1([0]*100 + [1, 1]))) 14.213872335107908 >>> min(timeit.repeat(lambda: only1([1, 1] + [0]*100))) 2.2482982632641324 >>> 2.2482/2.2080 1.0182065217391305 >>> 14.2138/13.9922 1.0158373951201385
Wir sehen also, dass die akzeptierte Antwort etwas länger dauert (etwas mehr als eineinhalb Prozent).
Natürlich ist die Verwendung des
any
in C geschriebenen integrierten Systems viel schneller (siehe Jon Clements Antwort für die Implementierung - dies ist die Kurzform):>>> min(timeit.repeat(lambda: single_true([0]*100 + [1, 1]))) 2.7257133318785236 >>> min(timeit.repeat(lambda: single_true([1, 1] + [0]*100))) 2.012824866380015
quelle
import collections def only_n(l, testval=True, n=1): counts = collections.Counter(l) return counts[testval] == n
Lineare Zeit. Verwendet die integrierte Counter-Klasse, mit der Sie die Anzahl überprüfen sollten.
Wenn Sie Ihre Frage erneut lesen, möchten Sie anscheinend überprüfen, ob es nur einen Wahrheitswert und keinen Wert
True
gibt. Versuche dies:import collections def only_n(l, testval=True, coerce=bool, n=1): counts = collections.Counter((coerce(x) for x in l)) return counts[testval] == n
Während Sie eine bessere Best-Case-Leistung erzielen können, hat nichts eine bessere Worst-Case-Leistung. Dies ist auch kurz und leicht zu lesen.
Hier ist eine Version, die für die beste Leistung optimiert ist:
import collections import itertools def only_n(l, testval=True, coerce=bool, n=1): counts = collections.Counter() def iterate_and_count(): for x in itertools.imap(coerce,l): yield x if x == testval and counts[testval] > n: break counts.update(iterate_and_count()) return counts[testval] == n
Die Worst-Case-Leistung ist hoch
k
(wie inO(kn+c)
), aber völlig allgemein.Hier ist eine Idee, um mit der Leistung zu experimentieren: http://ideone.com/ZRrv2m
quelle
Hier ist etwas, das für alles Wahrhaftige funktionieren sollte, obwohl es keinen Kurzschluss gibt. Ich habe es gefunden, als ich nach einem sauberen Weg gesucht habe, um sich gegenseitig ausschließende Argumente zu verbieten:
if sum(1 for item in somelist if item) != 1: raise ValueError("or whatever...")
quelle
Wie wäre es mit:
len([v for v in l if type(v) == bool and v])
Wenn Sie nur boolesche True-Werte zählen möchten.
quelle