switch-case
de la familia de lenguajes basados en C (C++, C#, Java, JavaScript, etc.). Otros lenguajes como Pascal, Ada, Perl, Ruby y diversos dialectos de Lisp cuentan también con alguna instrucción parecida. Sin embargo Python carece de esta instrucción, que de forma más general se le puede llamar “ramificación condicional con múltiples caminos”.El switch-case es una ramificación condicional con múltiples caminos. |
Una instrucción
switch-case
permite seleccionar, por medio de una expresión, el siguiente bloque de instrucciones a ejecutar de entre varios posibles. Veamos un ejemplo del switch-case
en JavaScript:
// Código de JavaScript function imprimeRangoCarta(n) { switch (n) { case 1: console.log('As'); break; case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: console.log(n); break; case 11: console.log('Jota'); break; case 12: console.log('Reina'); break; case 13: console.log('Rey'); break; default: console.log('Inválido'); break } }La intención de esta función es imprimir en la consola el rango de una carta de una baraja a partir de su valor numérico
n
, el cual se recibe como parámetro. Si n
vale 1, 11, 12 o 13 la función imprime “As”, “Jota”, “Reina” o “Rey”, respectivamente; imprime el valor de n
si dicha variable tiene un valor entre 2 y 10. Por último, imprime “Inválido” si n
tiene algún valor diferente a los ya mencionados.Las instrucciones
break
se requieren para concluir el switch
. Si se llega a omitir un break
, el programa continúa ejecutando las instrucciones asociadas al case
(o default
) que continúa inmediatamente; normalmente esto no es lo que se quiere.
La forma más directa de traducir nuestro código de JavaScript a Python es usando una secuencia de instrucciones
if-elif-else
:
# Código de Python 3 def imprime_rango_carta(n): if n == 1: print('As') elif 2 <= n <= 10: print(n) elif n == 11: print('Jota') elif n == 12: print('Reina') elif n == 13: print('Rey') else: print('Inválido')La instrucción
elif
es una contracción de las instrucciones else
e if
, con la ventaja de que no es necesario añadir un nivel de indentación al momento de utilizarla. Si no existiera la instrucción elif
, el código de arriba se tendría que escribir de una forma un tanto menos conveniente:# Código de Python 3 def imprime_rango_carta(n): if n == 1: print('As') else: if 2 <= n <= 10: print(n) else: if n == 11: print('Jota') else: if n == 12: print('Reina') else: if n == 13: print('Rey') else: print('Inválido')Vale la pena también notar que para determinar si
n
está entre 2 y 10 usamos la expresión:
2 <= n <= 10En otros lenguajes de programación tendríamos que escribir una expresión equivalente a la siguiente:
2 <= n and n <= 10Para las situaciones más comunes, se considera que el
switch-case
tiene al menos dos ventajas sobre una serie equivalente de if
s anidados:- Su sintaxis favorece la legibilidad y rapidez de escritura gracias a que el código generalmente es más breve y compacto.
- Corre más rápido debido a que usualmente, de manera interna, el ambiente de ejecución utiliza una tabla de acceso rápido para seleccionar el código a ejecutar, en lugar de ir haciendo una por una cada una de las comparaciones que requieren una serie de
if
s anidados.
switch-case
en el lenguaje. Sin embargo sí podemos aprovechar el punto 2, utilizando una tabla en Python para evitar múltiples comparaciones. La implementación de dicha tabla se puede hacer mediante una lista o un diccionario. En nuestro programa usaremos un diccionario. En la entrada titulada Código morse introduje el uso de diccionarios. Dado que los diccionarios en Python están implementados usando tablas hash, las búsquedas y accesos se ejecutan de manera muy veloz.Ahora bien, para la discusión que sigue es importante distinguir entre “invocar una función” y “hacer referencia al objeto que representa una función”. Esto queda más claro con un ejemplo. Sea
fun
una función sin parámetros que devuelve siempre el mismo valor:
def fun(): return 42Podemos usar
fun
básicamente de dos maneras:
a = fun b = fun()Hay que notar la presencia o ausencia de los paréntesis
()
después del identificador fun
. En el código anterior, a
contiene una referencia al objeto que representa la función fun
, mientras que b
contiene el resultado de invocar a la función fun
. Si imprimimos los valores de a
y b
con estas instrucciones:
print('a =', a) print('b =', b)veremos una salida como la siguiente:
a = <function fun at 0x4815162342> b = 42Esta salida extraña de
a
quiere decir que es un objeto función. De manera más precisa, a
es una referencia al objeto función llamado fun
. Dicho de otra forma, fun
y a
son alias, pues se refieren al mismo objeto. Dado que podemos decir que a
es una función, entonces la podemos invocar usando los paréntesis correspondientes y hacer algo con el resultado:
c = a()Como es de esperarse,
c
queda con 42.Lo que hay que recordar es esto:
- Si en una expresión viene el nombre de una función seguido de un par de paréntesis
()
, con o sin argumentos, significa que la función se está invocando. El resultado de esa parte de la expresión es lo que devuelva la función. - Si en una expresión viene el nombre de una función sin venir seguido de un par de paréntesis
()
, significa que se está obteniendo una referencia al objeto función siendo nombrado. El resultado de esa parte de la expresión es el objeto función correspondiente.
# Código de Python 3 def imprime_rango_carta(n): def caso_as(): print('As') def caso_num(): print(n) def caso_jota(): print('Jota') def caso_reina(): print('Reina') def caso_rey(): print('Rey') def caso_invalido(): print('Inválido') tabla = { 1: caso_as, 2: caso_num, 3: caso_num, 4: caso_num, 5: caso_num, 6: caso_num, 7: caso_num, 8: caso_num, 9: caso_num, 10: caso_num, 11: caso_jota, 12: caso_reina, 13: caso_rey } f = tabla.get(n, caso_invalido) f()Hay que notar varias cosas en el código anterior:
- El código de cada “cláusula
case
” se incorpora dentro de una función local. - La función local
caso_num
utiliza en su cuerpo el parámetron
de la función en la que está anidada. Esto es totalmente válido, y demuestra que Python soporta cerraduras léxicas (lexical closures). Esto último significa que al crear una función, todas las variables que son visibles en ese punto también lo son dentro del cuerpo de dicha función. - El objeto
tabla
se construye usando un diccionario. Las llaves son los valores de cada “cláusulacase
”, y éstas se asocian a sus correspondientes objetos función. - El método
get()
de la penúltima línea permite buscar una llave (el primer argumento:n
) en el diccionario receptor (tabla
) y regresar el valor asociado a dicha llave en caso de encontrarlo; si la llave no existe, regresa un valor por omisión (el segundo argumento: el objeto funcióncaso_invalido
). - A la variable
f
se le asigna el resultado del métodoget()
, el cual en todos los casos es un objeto función. - La última línea invoca la función obtenida en la instrucción anterior.
imprime_rango_carta
es imprimir un valor en particular a partir de lo que contenga n
, podemos hacer que el diccionario contenga solamente los valores que varían en cada uno de los distintos casos:# Código de Python 3 __tabla = { 1: 'As', 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10, 11: 'Jota', 12: 'Reina', 13: 'Rey' } def imprime_rango_carta(n): print(__tabla.get(n, 'Inválido'))Definimos la variable
__tabla
afuera de la función para que sea inicializada una vez y no cada vez que ésta sea invocada. Los dos subguiones al inicio de un nombre son una convención para indicar que la variable es privada a este módulo. También hay que notar que solamente usamos una instrucción print()
para abarcar todos los casos. Como se puede ver, el código queda más compacto y fácil de modificar que el switch-case
original.Moraleja: la instrucción
switch-case
es innecesaria en Python si sabemos usar diccionarios de manera correcta.