usar - Asignación elegante de Python basada en valores True/False
true y false en python (11)
¿Qué hay de usar un dict?
name = {(True, True, True): "first", (True, True, False): "second",
(True, False, True): "third", (True, False, False): "fourth",
(False, True, True): "fifth", (False, True, False): "sixth",
(False, False, True): "seventh", (False, False, False): "eighth"}
print name[a,b,c] # prints "fifth" if a==False, b==True, c==True etc.
Tengo una variable que quiero establecer dependiendo de los valores en tres booleanos. La forma más directa es una instrucción if seguida de una serie de elifs:
if a and b and c:
name = ''first''
elif a and b and not c:
name = ''second''
elif a and not b and c:
name = ''third''
elif a and not b and not c:
name = ''fourth''
elif not a and b and c:
name = ''fifth''
elif not a and b and not c:
name = ''sixth''
elif not a and not b and c:
name = ''seventh''
elif not a and not b and not c:
name = ''eighth''
Esto es un poco incómodo, y me pregunto si hay una forma más pitonica para manejar este problema. Un par de ideas vienen a la mente.
Diccionario de pirateo:
name = {a and b and c: ''first'', a and b and not c: ''second'', a and not b and c: ''third'', a and not b and not c: ''fourth'', not a and b and c: ''fifth'', not a and b and not c: ''sixth'', not a and not b and c: ''seventh'', not a and not b and not c: ''eighth''}[True]
Lo llamo hack porque no soy demasiado salvaje porque siete de las claves son falsas y se anulan entre sí.
Y / o magia
name = (a and b and c and ''first'' or a and b and not c and ''second'' or a and not b and c and ''third'' or a and not b and not c and ''fourth'' or not a and b and c and ''fifth'' or not a and b and not c and ''sixth'' or not a and not b and c and ''seventh'' or not a and not b and not c and ''eighth'')
Esto funciona porque Python, ands y ors devuelven el último valor para ser evaluado, pero debes saberlo para poder entender este extraño código.
Ninguna de estas tres opciones es muy satisfactoria. ¿Que recomiendas?
¿Qué pasa con los if anidados? Esto significa que no tiene que verificar todo varias veces y leer más claramente para mí (aunque quizás no sea tan inteligente como algunas de las otras respuestas):
if a:
if b:
if c:
name="first"
else:
name="second"
else:
if c:
name="third"
else:
name="fourth"
else:
if b:
if c:
name="fifth"
else:
name="sixth"
else:
if c:
name="seventh"
else:
name="eighth"
Aquí hay un enfoque de tabla de verdad:
lookup = {''000'': ''eighth'',
''001'': ''seventh'',
''010'': ''sixth'',
''011'': ''fifth'',
''100'': ''fourth'',
''101'': ''third'',
''110'': ''second'',
''111'': ''first''}
def key(a, b, c):
return ''''.join([str(a),str(b),str(c)])
name = lookup[key(0,1,1)]
Como obtiene todas las combinaciones, puede crear un índice basado en los valores como este:
def value(a,b,c ):
values = [''8th'',''7th'',''6th'',''5th'',''4th'',''3rd'',''2nd'',''1st'']
index = ( 4 if a else 0 ) + ( 2 if b else 0 ) + ( 1 if c else 0 )
return values[index]
if __name__ == "__main__":
print value(True, True, True )
print value(True, True, False )
print value(True, False, True )
print value(True, False, False )
print value(False, True, True )
print value(False, True, False)
print value(False, False, True )
print value(False, False, False)
salida:
1st
2nd
3rd
4th
5th
6th
7th
8th
Me gustaría ir a la solución de listas / bits de @OscarRyz, @Clint y @Sven, pero aquí hay otra:
def value(a, b, c):
for r in (a and S1 or empty) & (b and S2 or empty) & (c and S3 or empty):
return r
return last
S1 = frozenset([''first'', ''second'', ''third'', ''fourth''])
S2 = frozenset([''first'', ''second'', ''fifth'', ''sixth''])
S3 = frozenset([''first'', ''third'', ''fifth'', ''seventh''])
last = ''eighth''
empty = frozenset([])
Otra opción sería crear una función auxiliar:
def first_true(*args):
true_vals = (arg for arg in args if arg[0])
return next(true_vals)[1]
name = first_true((a and b and c, ''first''),
(a and b and not c, ''second''),
(a and not b and c, ''third''),
(a and not b and not c, ''fourth''),
(not a and b and c, ''fifth''),
(not a and b and not c, ''sixth''),
(not a and not b and c, ''seventh''),
(not a and not b and not c, ''eighth''))
Este método supone que una de las pruebas pasadas será verdadera. También podría hacerse más perezoso con lambdas.
Para medir velocidades:
from time import clock
a,b,c = True,False,False
A,B,C,D,E,F,G,H = [],[],[],[],[],[],[],[]
for j in xrange(30):
te = clock()
for i in xrange(10000):
name = (a and b and c and ''first'' or
a and b and not c and ''second'' or
a and not b and c and ''third'' or
a and not b and not c and ''fourth'' or
not a and b and c and ''fifth'' or
not a and b and not c and ''sixth'' or
not a and not b and c and ''seventh'' or
not a and not b and not c and ''eighth'')
A.append(clock()-te)
te = clock()
for i in xrange(10000):
if a and b and c:
name = ''first''
elif a and b and not c:
name = ''second''
elif a and not b and c:
name = ''third''
elif a and not b and not c:
name = ''fourth''
elif not a and b and c:
name = ''fifth''
elif not a and b and not c:
name = ''sixth''
elif not a and not b and c:
name = ''seventh''
elif not a and not b and not c:
name = ''eighth''
B.append(clock()-te)
#=====================================================================================
li = [''eighth'', ''seventh'', ''sixth'', ''fifth'', ''fourth'', ''third'', ''second'', ''first'']
te = clock()
for i in xrange(10000):
name = li[a*4 + b*2 + c]
C.append(clock()-te)
#=====================================================================================
nth = [''eighth'', ''seventh'', ''sixth'', ''fifth'', ''fourth'', ''third'', ''second'', ''first'']
te = clock()
for i in xrange(10000):
name = nth[(a and 4 or 0) | (b and 2 or 0) | (c and 1 or 0)]
D.append(clock()-te)
nth = [''eighth'', ''seventh'', ''sixth'', ''fifth'', ''fourth'', ''third'', ''second'', ''first'']
te = clock()
for i in xrange(10000):
name = nth[(a and 4 or 0) + (b and 2 or 0) + (c and 1 or 0)]
E.append(clock()-te)
#=====================================================================================
values = [''eighth'', ''seventh'', ''sixth'', ''fifth'', ''fourth'', ''third'', ''second'', ''first'']
te = clock()
for i in xrange(10000):
name = values[( 4 if a else 0 )| ( 2 if b else 0 ) | ( 1 if c else 0 )]
F.append(clock()-te)
values = [''eighth'', ''seventh'', ''sixth'', ''fifth'', ''fourth'', ''third'', ''second'', ''first'']
te = clock()
for i in xrange(10000):
name = values[( 4 if a else 0 ) + ( 2 if b else 0 ) + ( 1 if c else 0 )]
G.append(clock()-te)
#=====================================================================================
dic = {(True, True, True): "first",
(True, True, False): "second",
(True, False, True): "third",
(True, False, False): "fourth",
(False, True, True): "fifth",
(False, True, False): "sixth",
(False, False, True): "seventh",
(False, False, False): "eighth"}
te = clock()
for i in xrange(10000):
name = dic[a,b,c]
H.append(clock()-te)
print min(A),''/n'', min(B),''/n/n'', min(C),''/n/n'', min(D),''/n'',min(E),''/n/n'',min(F),''/n'', min(G),''/n/n'', min(H)
Resultado
0.0480533140385
0.0450973517584
0.0309056039245
0.0295291720037
0.0286550385594
0.0280122194301
0.0266760160858
0.0249769174574
Puedes pensar que a, byc son tres bits que, al juntarse, forman un número entre 0 y 7. Luego, puedes tener una matriz de los valores [''primero'', ''segundo'', ... ''octavo'' ] y usa el valor del bit como un desplazamiento en la matriz. Esto sería simplemente dos líneas de código (una para ensamblar los bits en un valor de 0-7 y otra para buscar el valor en la matriz).
Aquí está el código:
nth = [''eighth'', ''seventh'', ''sixth'', ''fifth'', ''fourth'', ''third'', ''second'', ''first'']
nth[(a and 4 or 0) | (b and 2 or 0) | (c and 1 or 0)]
Tal vez no mucho mejor, pero ¿qué tal
results = [''first'', ''second'', ''third'', ''fourth'',
''fifth'', ''sixth'', ''seventh'', ''eighth'']
name = results[((not a) << 2) + ((not b) << 1) + (not c)]
si a, b, c son realmente booleanos:
li = [''eighth'', ''seventh'', ''sixth'', ''fifth'', ''fourth'', ''third'', ''second'', ''first'']
name = li[a*4 + b*2 + c]
si no son booleanos:
li = [''eighth'', ''seventh'', ''sixth'', ''fifth'', ''fourth'', ''third'', ''second'', ''first'']
a,b,c = map(bool,(a,b,c))
name = li[a*4 + b*2 + c]
idea de Clint Miller
si su objetivo es evitar escribir muchos "ands" y expresiones booleanas, puede usar el número primo y solo una condición como esta (ejemplo para 2 condiciones)
cond = (2**cond_1)*(3**cond_2)
asi que
cond == 1 #means cond_1 and cond_2 are False
cond == 2 #means cond_1 is True and con_2 is False
cond == 3 #means cond_1 is False and con_2 is True
cond == 6 #means con_1 and Con_2 are True
Este truco se puede usar para 3 condiciones usando 3 primos, etc.
Me gusta esto...
cond = (2**a)*(3**b)*(5**c)
name = {30:''first'', 6: ''second'', 10:''third'', 2:''fourth'',
15:''fifth'', 3:''sixth'', 5:''seventh'', 1:''eighth''}[cond]