En Python, tengo una lista que debería tener uno y solo un valor de verdad (es decir, bool(value) is True). ¿Hay alguna forma inteligente de comprobarlo? En este momento, solo estoy iterando en la lista y verificando manualmente:
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
Esto parece poco elegante y no muy pitónico. ¿Existe una forma más inteligente de hacer esto?

(= 1 (count-if #'identity list)).sum(lst) == 1Trueo solo un valor de verdad?Respuestas:
La solución más detallada no siempre es la menos elegante. Por lo tanto, agrego solo una modificación menor (para guardar algunas evaluaciones booleanas redundantes):
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_foundAquí hay algunos tiempos para comparar:
# 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_foundMi salida:
$ 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 loopComo se puede ver, la solución OP es significativamente mejor que la mayoría de las otras soluciones publicadas aquí. Como era de esperar, los mejores son aquellos con comportamiento de cortocircuito, especialmente la solución publicada por Jon Clements. Al menos para el caso de dos
Truevalores iniciales en una lista larga.Aquí lo mismo sin ningún
Truevalor:$ 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 loopNo verifiqué la significancia estadística, pero curiosamente, esta vez los enfoques sugeridos por FJ y especialmente el de Jon Clements nuevamente parecen ser claramente superiores.
fuente
0.446el más rápido?anyse implementa en Csumson en realidad peores que el código simple y directo del OP ..Uno que no requiere importaciones:
def single_true(iterable): i = iter(iterable) return any(i) and not any(i)Alternativamente, quizás una versión más legible:
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_trueEsta:
itenga un valor realfuente
anysegún los documentos, devolverá True tan pronto como se encuentre un valor que no sea falso. Después de eso, buscamos un valor verdadero nuevamente, y si lo encontramos, lo tratamos como un error ... Entonces esto funcionará para listas vacías, listas / otras secuencias y cualquier iterable ...x and not x = Falsesolo es correcto sixes referencialmente transparente.any()- es una característica documentada de la función y la funcionalidad garantizada de cualquier aplicación que se ajusta a la especificación de Python.Depende si solo está buscando el valor
Trueo también está buscando otros valores que se evaluaríanTruelógicamente (como11o"hello"). Si el primero:def only1(l): return l.count(True) == 1Si es el último:
def only1(l): return sum(bool(e) for e in l) == 1ya que esto haría tanto el recuento como la conversión en una sola iteración sin tener que crear una nueva lista.
fuente
list(map(bool, l)).count(True)return sum(bool(e) for e in l) == 1.boollas subclasesinty Verdadero / Falso se comportan como 1/0 con respecto a la aritmética.l1sum(bool(e) for e in l)sum(1 for e in l if e)Una respuesta de una línea que conserva el comportamiento de cortocircuito:
from itertools import ifilter, islice def only1(l): return len(list(islice(ifilter(None, l), 2))) == 1Esto será significativamente más rápido que las otras alternativas aquí para iterables muy grandes que tienen dos o más valores verdaderos relativamente temprano.
ifilter(None, itr)da un iterable que solo producirá elementos veraces (xes veraz sibool(x)devuelveTrue).islice(itr, 2)da un iterable que solo producirá los dos primeros elementos deitr. Al convertir esto en una lista y verificar que la longitud sea igual a uno, podemos verificar que exista exactamente un elemento verdadero sin necesidad de verificar ningún elemento adicional después de haber encontrado dos.Aquí hay algunas comparaciones de tiempos:
Código de configuración:
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) == 1Exhibiendo comportamiento de cortocircuito:
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 loopLista grande donde no se produce un cortocircuito:
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 loopLista pequeña:
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 loopEntonces, el
sum()enfoque es más rápido para listas muy pequeñas, pero a medida que la lista de entrada aumenta, mi versión es más rápida incluso cuando no es posible un cortocircuito. Cuando es posible un cortocircuito en una entrada grande, la diferencia de rendimiento es clara.fuente
timeitexperimentación para una comparación objetiva de rendimiento con la solución OP.Truevalores en algún lugar "al principio", esto terminará, en comparación con las otras respuestas que hacen girar sus ruedas para siempre tratando de obtener la cuenta.Quería ganar la insignia de nigromante, así que generalicé la excelente respuesta de Jon Clements, preservando los beneficios de la lógica de cortocircuito y la verificación rápida de predicados con todos y cada uno.
Así que aquí está:
N (verdades) = n
def n_trues(iterable, n=1): i = iter(iterable) return all(any(i) for j in range(n)) and not any(i)N (verdades) <= 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 (verdades)> = n:
def at_least_n_trues(iterable, n=1): i = iter(iterable) return all(any(i) for j in range(n))m <= N (verdaderos) <= 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)fuente
>>> l = [0, 0, 1, 0, 0] >>> has_one_true = len([ d for d in l if d ]) == 1 >>> has_one_true Truefuente
Tu puedes hacer:
x = [bool(i) for i in x] return x.count(True) == 1O
x = map(bool, x) return x.count(True) == 1Sobre la base del método de @ JoranBeasley:
sum(map(bool, x)) == 1fuente
if sum([bool(x) for x in list]) == 1(Suponiendo que todos sus valores son booleanos).
Esto probablemente sería más rápido simplemente resumiéndolo
sum(list) == 1aunque puede causar algunos problemas según los tipos de datos de su lista.
fuente
Si solo hay uno
True, entonces la longitud de laTrues debe ser uno:def only_1(l): return 1 == len(filter(None, l))fuente
Esto parece funcionar y debería poder manejar cualquier iterable, no solo
lists. Se produce un cortocircuito siempre que sea posible para maximizar la eficiencia. Funciona tanto en Python 2 como en 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])))Salida:
fuente
iter(x for x in my_list if x)y luego usarnext, tal vez más agradable que usarmapy?list.indexmapylist.index.La solución de @ JonClements se extendió para como máximo N valores verdaderos :
# 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)editar: mejor versión
def test(iterable, n=1): i = iter(iterable) return sum(any(i) for x in xrange(n+1)) <= nedit2: incluya al menos m True y como máximo n True
def test(iterable, n=1, m=1): i = iter(iterable) return m <= sum(any(i) for x in xrange(n+1)) <= nfuente
iterable.count(True) = 3,NTrue(iterable, 1) = False,NTrue(iterable, 2) = False,NTrue(iterable, 3) = True,NTrue(iterable, 4) = True, ... Básicamente se extiende laand not any(i)parte deand not any(i) and not any(i) and not...all(any(i) for i in xrange(n)) and not any(i)funciona aquí?anys.any(i) and not all(any(i) for x in xrange(n))?True and not all(<n booleans>)lógicamente lo mismo quecount(True) <= n? La idea sigue siendo probar el conjunto más pequeño posible y romper en la primera condición de falla.def only1(l) sum(map(lambda x: 1 if x else 0, l)) == 1Explicación: La
mapfunción asigna una lista a otra lista, haciendoTrue => 1yFalse => 0. Ahora tenemos una lista de ceros y unos en lugar de verdadero o falso. Ahora simplemente sumamos esta lista y si es 1, solo había un valor Verdadero.fuente
¿Es esto lo que estás buscando?
sum(l) == 1fuente
En aras de la integridad y para demostrar el uso avanzado del flujo de control de Python para la iteración del bucle for, se puede evitar la contabilidad adicional en la respuesta aceptada, lo que hace que esto sea un poco más rápido:
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.El flujo de control simple anterior hace uso de la característica sofisticada de bucles de Python: el
else. La semántica es que si termina de iterar sobre el iterador que está consumiendo sinbreaksalir de él, ingresa alelsebloque.Aquí está la respuesta aceptada, que usa un poco más de contabilidad.
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_foundpara cronometrar estos:
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.0158373951201385Entonces vemos que la respuesta aceptada toma un poco más de tiempo (un poco más del 1,5%).
Naturalmente, usar el integrado
any, escrito en C, es mucho más rápido (consulte la respuesta de Jon Clement para la implementación; esta es la forma corta):>>> min(timeit.repeat(lambda: single_true([0]*100 + [1, 1]))) 2.7257133318785236 >>> min(timeit.repeat(lambda: single_true([1, 1] + [0]*100))) 2.012824866380015fuente
import collections def only_n(l, testval=True, n=1): counts = collections.Counter(l) return counts[testval] == nTiempo lineal. Utiliza la clase Counter incorporada, que es lo que debería utilizar para comprobar los recuentos.
Al volver a leer su pregunta, parece que realmente desea verificar que solo haya un valor verdadero, en lugar de un
Truevalor. Prueba esto:import collections def only_n(l, testval=True, coerce=bool, n=1): counts = collections.Counter((coerce(x) for x in l)) return counts[testval] == nSi bien puede obtener un mejor rendimiento en el mejor de los casos, nada tiene un mejor rendimiento en el peor de los casos. También es breve y fácil de leer.
Aquí hay una versión optimizada para el mejor rendimiento de los casos:
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] == nEl peor de los casos tiene un rendimiento alto
k(como enO(kn+c)), pero es completamente general.Aquí hay una idea para experimentar con el rendimiento: http://ideone.com/ZRrv2m
fuente
Aquí hay algo que debería funcionar para cualquier cosa veraz, aunque no tiene cortocircuito. Lo encontré mientras buscaba una forma limpia de prohibir argumentos mutuamente excluyentes:
if sum(1 for item in somelist if item) != 1: raise ValueError("or whatever...")fuente
Qué pasa:
len([v for v in l if type(v) == bool and v])Si solo desea contar valores booleanos verdaderos.
fuente