Durante el semestre académico que recién terminó (agosto-diciembre de 2016) tuve la oportunidad de dar el curso de Fundamentos de programación. Esta materia la he impartido pocas veces, sin embargo los aprendizajes que me llevo como educador siempre son muy valiosos. Es muy interesante trabajar con alumnos que recién comienzan su carrera profesional y que también están iniciando su inmersión al mundo de la programación de computadoras.
Grupo 4 de la materia Fundamentos de programación del ITESM CEM, semestre agosto-diciembre de 2016. |
Cuando se comienza a aprender algo nuevo es de esperarse que se cometerán errores. Como dijo alguna vez el renombrado escritor irlandés Bram Stoker:
Aprendemos de los fracasos, no de los éxitos.Errar es humano, y en esta entrada del blog de EduPython comentaré, a partir de mi experiencia, sobre los cuatro errores más frecuentes que cometen los alumnos cuando están aprendiendo a programar usando el lenguaje Python, así como algunas recomendaciones de cómo evitarlos.
Paréntesis mal balanceados
Este es el error más común que he observado al inicio del curso. Se tiene una instrucción la cual requiere del uso de paréntesis anidados, por ejemplo:print(sqrt(x ** 2 + y ** 2)En el código anterior se puede observar que está faltando cerrar un paréntesis al final. Al intentar ejecutarlo, el intérprete de Python marca usualmente un error de sintaxis. La cuestión es que dicho error se marca en la siguiente línea, no en la línea donde está realmente el problema.
Aquí recomiendo dos cosas:
- Acostumbrar a los alumnos a buscar los errores de sintaxis no solo en la línea donde se señala el problema, sino también una o varias líneas antes.
- Enseñar a los alumnos a comprender y utilizar las pistas visuales que brindan los editores al momento de cerrar paréntesis, llaves y corchetes. En el caso del IDLE, cuando se cierra alguno de estos elementos (paréntesis, llaves y corchetes) se ensombrece toda la expresión desde el elemento correspondiente de apertura. En la siguiente imagen puede uno notar que falta cerrar el paréntesis de la función
print()
:IDLE
Otros editores, como Spyder, parpadean o usan un color de fondo diferente para destacar los paréntesis que se están abriendo y/o cerrando:
Spyder |
Indentación incorrecta
En Python la indentación sirve para indicar que un grupo de instrucciones conforman un mismo bloque. Este efecto se logra con llaves ({
y }
) o las palabras reservadas begin
y end
en otros lenguajes.La indentación obligatoria de Python es algo positivo, ya que obliga a los alumnos a adquirir prácticas de escritura de código legible. Normalmente los editores para código de Python ayudan a producir programas con la indentación correcta sin requerir mucho esfuerzo. Sin embargo se pueden producir errores cuando se copia y pega código de otro lado o cuando se insertan o borran espacios de manera no intencional, por ejemplo:
Programa con error de indentación |
En el programa de arriba, la línea 5 está indentada con un espacio más hacia la derecha con respecto a las otras líneas del mismo bloque (las líneas 3 y 8). El editor, Spyder en este caso, nos echa la mano al señalar que hay un problema en el programa colocando un signo de admiración en el margen izquierdo en la línea infractora.
Mis recomendaciones para evitar este error:
- Enfatizar a los alumnos sobre la importancia que tiene la indentación y la correcta alineación de las instrucciones para establecer la estructura lógica de sus programas. Si van a copiar y pegar código, se deben asegurar que todas las instrucciones queden alineadas en el lugar debido.
- Nunca mezclar en un mismo programa caracteres de espacio (código ASCII 32) con caracteres de tabulador (código ASCII 9). Esto no debe ser problema en la mayoría de los editores que soportan Python. Cuando el usuario presiona la tecla «Tab», usualmente los editores insertan cuatro espacios en lugar de insertar el carácter de tabulador. Sin embargo este comportamiento se puede cambiar en la configuración de la mayoría de los editores. Yo recomiendo no alterar la configuración en este sentido ya que no es fácil distinguir a primera vista un tabulador de una secuencia de varios espacios.
Uso incorrecto de
return
Las funciones son uno de los mecanismos de abstracción más poderosos que tenemos disponibles en Python y en casi cualquier otro lenguaje de programación. Yo generalmente cubro este tema desde muy temprano en el curso de Fundamentos de programación. Cuando los alumnos comienzan a escribir sus propias funciones el principal error que observo es el uso incorrecto de la instrucción return
. Los errores caen en alguno de los siguientes dos casos:return
inexistente. Esto sucede cuando una función implementa correctamente un cierto algoritmo, pero carece de la instrucciónreturn
necesaria para devolver el resultado correspondiente. Por ejemplo, la siguiente función cuenta la cantidad de números negativos contenidos en una lista:def cuenta_negativos(a): resultado = 0 for x in a: if x < 0: resultado += 1
En Python todas las funciones siempre devuelven un valor. Por omisión devuelvenNone
, a menos que explícitamente se indique otro valor dentro de una instrucciónreturn
. En el ejemplo anterior no hay unreturn
explícito, así que la función siempre devuelveNone
. Por tanto, la forma correcta de escribircuenta_negativos
es:def cuenta_negativos(a): resultado = 0 for x in a: if x < 0: resultado += 1 return resultado
Una variación de este problema es cuando una función tiene varias condiciones que utilizan instruccionesreturn
pero no son exhaustivas. Por ejemplo, analicemos la siguiente función:def positivo_o_negativo(x): if x < 0: return 'negativo' if x > 0: return 'positivo'
Esta función devuelve las cadenas'negativo'
o'positivo'
si el parámetrox
es menor o mayor a cero, respectivamente. Sin embargo, ¿qué pasa six
es igual a cero? Tenemos aquí un código en donde las condiciones no son exhaustivas produciendo un bug muy sutil. Al no cumplirse alguna de las dos condiciones la función termina sin ejecutar unreturn
explícito, por tanto devuelveNone
. Hay varias formas de corregir el problema. Una de ellas consiste en suponer que si un número no es negativo debe ser entonces positivo. Agregando una cláusulaelse
alif
tenemos:def positivo_o_negativo(x): if x < 0: return 'negativo' else: return 'positivo'
Con este cambio la función jamás devolveráNone
siempre quex
sea un valor numérico.return
fuera de lugar. En este caso el error consiste en incluir la instrucciónreturn
dentro de la función pero en un lugar que hace que el flujo de ejecución termine de manera prematura. Continuando con el ejemplo de la funcióncuenta_negativos
, a menudo veo que algunos alumnos escriben erróneamente el código así:def cuenta_negativos(a): resultado = 0 for x in a: if x < 0: resultado += 1 return resultado
La instrucciónreturn
se ejecutará cuando la condición delif
resulte verdadera. Elreturn
provoca que la función termine inmediatamente, por lo que las iteraciones pendientes del ciclofor
ya no se ejecutarán. En otras palabras, la función devuelve el valor 1 en cuanto encuentra la primer número negativo en la listaa
. Sia
no tiene números negativos elfor
concluye de manera normal, y como no hay posteriormente unreturn
explícito entonces devuelveNone
. Una variación, igualmente incorrecta, es ésta:def cuenta_negativos(a): resultado = 0 for x in a: if x < 0: resultado += 1 return resultado
Aquí elreturn
es parte del bloque delfor
. Esto quiere decir que la función siempre termina en la primera iteración del ciclo, devolviendo uno si el primer número dea
es negativo, o cero en caso contrario. Como caso especial, si la listaa
está vacía devuelveNone
, ya que el ciclofor
termina de manera normal y después no hayreturn
explícito. Podemos observar que la única diferencia sintáctica entre la versión correcta y los dos códigos erróneos es el nivel de indentación que tiene la instrucciónreturn
.
return
recomiendo:- Resaltarle a los alumnos la manera en que trabaja la instrucción
return
. Es muy importante que entiendan que cuando se ejecuta esta instrucción la función termina de manera inmediata sin importar que estuviera en medio de un ciclo o que existieran otras instrucciones posteriores pendientes a ser ejecutadas. Si una función está devolviendoNone
seguramente está haciendo falta unreturn
explícito en algún lado. - Promover que los alumnos realicen pruebas de escritorio de sus funciones, utilizando diferentes valores de entrada y procurando revisar de manera exhaustiva los casos normales y extremos.
Variables fuera de alcance
Para definir una variable en Python basta con asignarle un valor inicial. Si esta asignación ocurre dentro de una función, la variable en cuestión es local a dicha función y eso significa que no se puede acceder a ella desde otras funciones. Las variables locales son algo bueno ya que tienen un alcance y tiempo de vida limitado a una región usualmente reducida de código. Esto conlleva a que una función sea, en principio, más fácil de entender y modificar de manera aislada. Sin embargo, es bastante común que algunos principiantes intenten acceder a variables que están fuera de alcance (definidas en otra función), por ejemplo:from math import sqrt def hipotenusa(): return sqrt(a ** 2 + b ** 2) def principal(): a = float(input('Primer cateto: ')) b = float(input('Segundo cateto: ')) c = hipotenusa() print('La hipotenusa es', c)La función
principal()
define tres variables locales: a
, b
y c
. La función hipotenusa()
intenta usar dos de esas variables (a
y b
) produciendo un error ya que no están visibles en ese contexto. La forma más sencilla de corregir este problema es enviar como parámetros aquellos datos que deseemos compartir:
from math import sqrt def hipotenusa(a, b): return sqrt(a ** 2 + b ** 2) def principal(): a = float(input('Primer cateto: ')) b = float(input('Segundo cateto: ')) c = hipotenusa(a, b) print('La hipotenusa es', c)Los parámetros
a
y b
de la función hipotenusa()
reciben una copia de los valores de las variables locales a
y b
de la función principal()
. Aquí utilizamos los mismos nombres (a
y b
) en ambas funciones por mera conveniencia, pero no tiene que ser así.Mi recomendación para evitar problemas con variables fuera de alcance consiste en hacerles entender a los estudiantes cuáles son las implicaciones de que una variable sea local a una función. Así mismo, es necesario explicarles que cuando un programa requiere compartir información entre dos funciones se pueden utilizar parámetros para enviar datos de entrada y la instrucción
return
para devolver datos de salida. Esta es generalmente la manera más adecuada de diseñar un programa pues lo hace más legible y facilita su mantenimiento. Alternativamente, es posible usar variables globales o incluso definir funciones más extensas (por ejemplo unir hipotenusa()
y principal()
en una sola función). Sin embargo estas opciones provocan normalmente que el código sea más complicado de entender y modificar, por lo que conviene disuadir a los estudiantes de hacer esto, especialmente en las primeras etapas de un curso introductorio de programación.Conclusión
En mi experiencia, los problemas más usuales que tienen los estudiantes al comenzar a programar en Python se pueden resumir en:- Falta de comprensión clara sobre algunos conceptos fundamentales (indentación, instrucción
return
, alcance de las variables). - Falta de conocimiento de cómo usar las herramientas disponibles (principalmente el editor).
Amigo lector, si eres maestro y enseñas a programar, ¿qué tipo de errores has visto que tus alumnos cometen frecuentemente? ¿Cómo le haces para evitar que los cometan? Usa la sección de comentarios para compartir tus experiencias. Gracias de antemano.