python - ejemplos - django
¿Cómo puedo encontrar el valor que falta de manera más concisa? (11)
Creo que debería ser así:
z = (set(("a", "b", "c")) - set((x, y))).pop() if x != y else None
El siguiente código verifica si y
son valores distintos (las variables x
, y
, z
solo pueden tener los valores a
, b
, o c
) y, de ser así, establece z
al tercer carácter:
if x == ''a'' and y == ''b'' or x == ''b'' and y == ''a'':
z = ''c''
elif x == ''b'' and y == ''c'' or x == ''c'' and y == ''b'':
z = ''a''
elif x == ''a'' and y == ''c'' or x == ''c'' and y == ''a'':
z = ''b''
¿Es posible hacer esto de una manera más concisa, legible y eficiente?
Creo que la solución de Sven Marnach y FJ es hermosa, pero no es más rápida en mi pequeña prueba. Esta es la versión optimizada de Raymond que usa un conjunto pre-calculado:
$ python -m timeit -s "choices = set(''abc'')" /
-s "x = ''c''" /
-s "y = ''a''" /
"z, = choices - set(x + y)"
1000000 loops, best of 3: 0.689 usec per loop
Esta es la solución original:
$ python -m timeit -s "x = ''c''" /
-s "y = ''a''" /
"if x == ''a'' and y == ''b'' or x == ''b'' and y == ''a'':" /
" z = ''c''" /
"elif x == ''b'' and y == ''c'' or x == ''c'' and y == ''b'':" /
" z = ''a''" /
"elif x == ''a'' and y == ''c'' or x == ''c'' and y == ''a'':" /
" z = ''b''"
10000000 loops, best of 3: 0.310 usec per loop
Tenga en cuenta que esta es la peor entrada posible para las declaraciones if
ya que las seis comparaciones deberán probarse. La prueba con todos los valores para y
proporciona:
x = ''a'', y = ''b'': 0.084 usec per loop
x = ''a'', y = ''c'': 0.254 usec per loop
x = ''b'', y = ''a'': 0.133 usec per loop
x = ''b'', y = ''c'': 0.186 usec per loop
x = ''c'', y = ''a'': 0.310 usec per loop
x = ''c'', y = ''b'': 0.204 usec per loop
La variante basada en set
muestra el mismo rendimiento para diferentes entradas, pero es constantemente entre 2 y 8 veces más lento . La razón es que la variante basada en if
ejecuta un código mucho más simple: pruebas de igualdad en comparación con hashing.
Creo que ambos tipos de soluciones son valiosas: es importante saber que la creación de estructuras de datos "complicadas" como los juegos te cuesta algo en rendimiento, mientras que te dan mucho en legibilidad y velocidad de desarrollo . Los tipos de datos complejos también son mucho mejores cuando cambia el código: es fácil extender la solución basada en conjuntos a cuatro, cinco ... variables, mientras que las declaraciones if se convierten rápidamente en una pesadilla de mantenimiento.
El excelente código de Sven hizo demasiado trabajo y debió haber usado desempaquetado de tuplas en lugar de pop () . Además, podría haber agregado un guardia if x != y
para verificar que xey sean distintos. Aquí se muestra la respuesta mejorada:
# create the set just once
choices = {''a'', ''b'', ''c''}
x = ''a''
y = ''b''
# the main code can be used in a loop
if x != y:
z, = choices - {x, y}
Estos son los tiempos comparativos con un conjunto de tiempo para mostrar el rendimiento relativo:
import timeit, itertools
setup_template = ''''''
x = %r
y = %r
choices = {''a'', ''b'', ''c''}
''''''
new_version = ''''''
if x != y:
z, = choices - {x, y}
''''''
original_version = ''''''
if x == ''a'' and y == ''b'' or x == ''b'' and y == ''a'':
z = ''c''
elif x == ''b'' and y == ''c'' or x == ''c'' and y == ''b'':
z = ''a''
elif x == ''a'' and y == ''c'' or x == ''c'' and y == ''a'':
z = ''b''
''''''
for x, y in itertools.product(''abc'', repeat=2):
print ''/nTesting with x=%r and y=%r'' % (x, y)
setup = setup_template % (x, y)
for stmt, name in zip([original_version, new_version], [''if'', ''set'']):
print min(timeit.Timer(stmt, setup).repeat(7, 100000)),
print ''/t%s_version'' % name
Aquí están los resultados de los tiempos:
Testing with x=''a'' and y=''a''
0.0410830974579 original_version
0.00535297393799 new_version
Testing with x=''a'' and y=''b''
0.0112571716309 original_version
0.0524711608887 new_version
Testing with x=''a'' and y=''c''
0.0383319854736 original_version
0.048309803009 new_version
Testing with x=''b'' and y=''a''
0.0175108909607 original_version
0.0508949756622 new_version
Testing with x=''b'' and y=''b''
0.0386209487915 original_version
0.00529098510742 new_version
Testing with x=''b'' and y=''c''
0.0259420871735 original_version
0.0472128391266 new_version
Testing with x=''c'' and y=''a''
0.0423510074615 original_version
0.0481910705566 new_version
Testing with x=''c'' and y=''b''
0.0295209884644 original_version
0.0478219985962 new_version
Testing with x=''c'' and y=''c''
0.0383579730988 original_version
0.00530385971069 new_version
Estos tiempos muestran que el rendimiento de la versión original varía bastante dependiendo de qué enunciados if se desencadenan por los diversos valores de entrada.
El método strip
es otra opción que funciona rápidamente para mí:
z = ''abc''.strip(x+y) if x!=y else None
Mira si esto funciona
if a not in xy
z= ''a''
if b not in xy
z=''b''
if c not in xy
z=''c''
Pruebe esta opción, usando diccionarios:
z = {''ab'':''c'', ''ba'':''c'', ''bc'':''a'', ''cb'':''a'', ''ac'':''b'', ''ca'':''b''}[x+y]
Por supuesto, si la clave x+y
no está presente en el mapa, producirá un KeyError
que tendrás que manejar.
Si el diccionario se calcula previamente una sola vez y se almacena para su uso futuro, el acceso será mucho más rápido, ya que no se necesitarán crear nuevas estructuras de datos para cada evaluación, solo se necesita una concatenación de cadenas y una búsqueda del diccionario:
lookup_table = {''ab'':''c'', ''ba'':''c'', ''bc'':''a'', ''cb'':''a'', ''ac'':''b'', ''ca'':''b''}
z = lookup_table[x+y]
Si los tres elementos en cuestión no eran "a"
, "b"
y "c"
, sino 1
, 2
y 3
, también podría usar un XOR binario:
z = x ^ y
De manera más general, si desea establecer z
en el número restante de tres números a
, c
dados dos números y
de este conjunto, puede usar
z = x ^ y ^ a ^ b ^ c
Por supuesto, puede precalcular a ^ b ^ c
si los números son fijos.
Este enfoque también se puede usar con las letras originales:
z = chr(ord(x) ^ ord(y) ^ 96)
Ejemplo:
>>> chr(ord("a") ^ ord("c") ^ 96)
''b''
No esperes que nadie que lea este código descubra inmediatamente lo que significa :)
Usar la comprensión de listas, asumiendo como otros que uno de los tres casos en su código es válido:
l = [''a'', ''b'', ''c'']
z = [n for n in l if n not in [x,y]].pop()
O, como en la respuesta aceptada, aprovechando la tupla para descomprimirla,
z, = [n for n in l if n not in [x,y]]
z = ''a''*(''a'' not in x+y) or ''b''*(''b'' not in x+y) or ''c''
o menos hackish y usando la asignación condicional
z = ''a'' if (''a'' not in x+y) else ''b'' if (''b'' not in x+y) else ''c''
pero probablemente la solución dict sea más rápida ... tendrías que cronometrarla.
z = (set(''abc'') - set(x + y)).pop()
Aquí están todos los escenarios para mostrar que funciona:
>>> (set(''abc'') - set(''ab'')).pop() # x is a/b and y is b/a
''c''
>>> (set(''abc'') - set(''bc'')).pop() # x is b/c and y is c/b
''a''
>>> (set(''abc'') - set(''ac'')).pop() # x is a/c and y is c/a
''b''
z = (set(("a", "b", "c")) - set((x, y))).pop()
Estoy asumiendo que uno de los tres casos en su código es válido. Si este es el caso, el conjunto set(("a", "b", "c")) - set((x, y))
consistirá en un único elemento, que se devuelve mediante pop()
.
Editar: Como sugirió Raymond Hettinger en los comentarios, también puede usar el desempaquetado de tuplas para extraer el elemento único del conjunto:
z, = set(("a", "b", "c")) - set((x, y))