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) == 1
True
o 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_found
Aquí 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_found
Mi 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 loop
Como 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
True
valores iniciales en una lista larga.Aquí lo mismo sin ningún
True
valor:$ 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
No 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.446
el más rápido?any
se implementa en Csum
son 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_true
Esta:
i
tenga un valor realfuente
any
segú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 = False
solo es correcto six
es 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
True
o también está buscando otros valores que se evaluaríanTrue
lógicamente (como11
o"hello"
). Si el primero:def only1(l): return l.count(True) == 1
Si es el último:
def only1(l): return sum(bool(e) for e in l) == 1
ya 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
.bool
las subclasesint
y Verdadero / Falso se comportan como 1/0 con respecto a la aritmética.l
1
sum(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))) == 1
Esto 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 (x
es 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) == 1
Exhibiendo 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 loop
Lista 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 loop
Lista 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 loop
Entonces, 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
timeit
experimentación para una comparación objetiva de rendimiento con la solución OP.True
valores 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 True
fuente
Tu puedes hacer:
x = [bool(i) for i in x] return x.count(True) == 1
O
x = map(bool, x) return x.count(True) == 1
Sobre la base del método de @ JoranBeasley:
sum(map(bool, x)) == 1
fuente
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) == 1
aunque puede causar algunos problemas según los tipos de datos de su lista.
fuente
Si solo hay uno
True
, entonces la longitud de laTrue
s 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
list
s. 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 usarmap
y?list.index
map
ylist.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)) <= n
edit2: 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)) <= n
fuente
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í?any
s.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)) == 1
Explicación: La
map
función asigna una lista a otra lista, haciendoTrue => 1
yFalse => 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) == 1
fuente
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 sinbreak
salir de él, ingresa alelse
bloque.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_found
para 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.0158373951201385
Entonces 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.012824866380015
fuente
import collections def only_n(l, testval=True, n=1): counts = collections.Counter(l) return counts[testval] == n
Tiempo 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
True
valor. 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] == n
Si 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] == n
El 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