Saltar a contenido

Implementación de una función definida por el usuario

Recién acabamos de crear nuestra función hola():

Python
def hola():
    print("¡Hola!")

¿Dónde colocar las funciones definidas por el usuario?

Ahora queremos agregar esta función a nuestro programa principal, pero ¿dónde deberíamos colocarla?

Supongamos lo siguiente:

Python
1
2
3
4
5
# Código principal
nombre = input("¿Cuál es tu nombre? ")
hola()
print(nombre)

Python

# Definiciones de funciones de usuario
def hola():
    print("¡Hola!")

Uno de los aspectos importantes de definir sus propias funciones es que la escribes una vez y la puedes utilizar una y mil veces durante tu programa.

Y estaría muy bueno que, ahora que tu función está definida, te olvides de como es su funcionamiento y que te limites simplemente a llamarla en cualquier parte de tu programa. Y entonces puedas sacarla de tu vista, digamos, bajarla varias líneas de código para que no te entorpezca tu programa principal. Pero, ¿qué pasa si la colocas al final de tu programa? ¿Funcionará? ¿O deberías colocarla al principio? ¿O en medio de tu programa? ¿O en cualquier parte? ¿O en todas partes? ¿O en ninguna parte?

Si volvemos al código de arriba y lo ejecutamos, obtendremos un error similar al siguiente:

Terminal (Entrada/Salida)
¿Cuál es tu nombre? Pablo
Traceback (most recent call last):
  File "c:\Users\usuario\hola_mundo.py", line 2, in <module>
    hola()
    ^^^^
NameError: name 'hola' is not defined

¿Por qué ocurre esto? Porque la función hola() no está definida en el programa en el momento en que se la llama.

En este punto, aunque podemos dar por sentado que la función hola() existe, incluso estando más abajo en el archivo del programa o, como veremos más adelante, incluso si está en otro archivo completamente diferente, en el instante que se ejecute el programa con el intérprete de Python y el control del flujo pase por la primera línea de código que llame a la función, obtendremos un error de nombre (NameError en inglés) que nos indicará que hola() no está definido.

El problema aquí, es que Python interpreta las líneas de código de arriba hacia abajo, de izquierda a derecha. Por lo tanto, no puede saber de la existencia de una función definida en una línea posterior a la primera línea que la llame por el simple hecho de que aún no ha llegado a esa línea de código.

El intérprete de Python si usa una función, esta ya debe existir, es decir estar definida, en el momento en que sea llamada.

¡Para recordar!

Las funciones definidas por el usuario en Python se pueden agregar en cualquier parte del programa, siempre que se encuentren antes que en la línea donde sean llamadas por primera vez, pero es una buena práctica colocarlas al principio del programa, antes de cualquier otra instrucción que las invoque.

De esta manera, se asegura que la función esté definida antes de ser llamada, así como también se mantiene el código más organizado y fácil de leer.

Con esta buena práctica en mente, repasemos ahora como queda nuestro programa:

Python
1
2
3
4
5
6
7
8
9
# Definiciones de funciones de usuario
def hola():
    print("¡Hola!")


# Código principal
nombre = input("¿Cuál es tu nombre? ")
hola()
print(nombre)

Llamada a función definida por el usuario

Se denomina llamada a función (call function en inglés) al momento en que se invoca a una función definida por el usuario en un programa pasando los argumentos necesarios, si es que los requiere. En ese momento, el flujo de ejecución del programa se desplaza al bloque de código de la función y se ejecutan las instrucciones que contiene.

Al finalizar su ejecución, por el motivo que fuere, la función puede retornar un valor o simplemente finalizar su ejecución sin devolver nada. En ese momento, el flujo de ejecución del programa regresa al punto donde se invocó la función y continúa su ejecución normal.

Si retorna un valor, este puede ser almacenado en una variable o utilizado directamente en el programa, según sea necesario. El destino de ese valor dependerá de la lógica del programa en la línea de código que invocó a la función.

Ejecución de la función definida por el usuario

Siguiendo con nuestro programa, en la línea 1 definimos la función hola(), que simplemente imprime un mensaje de saludo en pantalla.

Luego, en la línea 7, solicitamos al usuario que ingrese su nombre y lo almacenamos en la variable nombre.

A continuación, en la línea 8, invocamos a la función hola(), lo que significa que se ejecutará el bloque de código que contiene la función a partir de la línea 3 que en este caso es solo esa línea, cuya tarea es imprimir el mensaje de saludo en pantalla.

Finalmente, en la línea 9, imprimimos en pantalla el nombre ingresado por el usuario.

Terminal (Entrada/Salida)
¿Cuál es tu nombre? Pablo
¡Hola!
Pablo

Y así concluye la ejecución de nuestro programa, que ahora incluye una función definida por el usuario que imprime un mensaje de saludo en pantalla.

A ver si entendiste: ¿nuestro programa contiene una función o una subrutina?
Haz clic aquí para conocer la respuesta
  • [ ] Función
  • [x] Subrutina

Las subrutinas son bloques de código que realizan una tarea específica y pueden ser llamadas y ejecutadas desde otros lugares en el programa.

En Python, las subrutinas se implementan de igual manera que las funciones.

La diferencia principal entre una función y una subrutina es que una función retorna (return en inglés) un valor (resultado), mientras que una subrutina no devuelve nada.

Y nuestra función hola() no retorna ningún valor, solo imprime un mensaje en pantalla, por lo que técnicamente es una subrutina y no una función.

¡Para recordar!

Una función es un bloque de código que realiza una tarea específica y puede ser reutilizada en diferentes partes de un programa.

Tiene un nombre que la identifica seguido de paréntesis, y puede recibir argumentos que almacenará en parámetros que se utilizan dentro de la función para realizar su tarea.

Al finalizar su tarea, puede devolver un valor o simplemente finalizar su ejecución sin devolver nada (en este último caso, no sería una función si no una subrutina1).

Parametrización de subrutinas y funciones

¿Por qué no consideramos cómo parametrizar esta subrutina?

Es decir, ¿podemos personalizar el saludo para que tal vez tome el nombre del usuario como entrada y así poder no solo decir "¡Hola!" y en la siguiente línea el nombre de la persona, sino también agregar al "¡Hola!" el nombre de la persona y hacerlo todo en una línea?

Claro que podemos. Para ello, hagamos la siguiente modificación a nuestro código:

Python
1
2
3
4
5
6
7
8
# Definiciones de funciones de usuario
def hola(nombre):
    print(f"Hola, {nombre}!")


# Código principal
nombre = input("¿Cuál es tu nombre? ")
hola(nombre)

En este caso, primero se ingresa el nombre del usuario y se almacena en la variable nombre, en la línea 7.

Luego, se invoca a la función hola() pasando como argumento el valor almacenado en la variable nombre, en la línea 8.

En este caso, la función hola() de la línea 2 recibe el argumento que es el nombre de la persona a la que se le dirige el saludo y lo almacena en el parámetro nombre, en el contexto de la función.

Es importante remarcar que el parámetro nombre de la función hola() es local a la función, lo que significa que solo existe dentro de la función y no afecta a la variable nombre del programa principal. Aunque se llamen igual, son dos variables diferentes que existen en contextos diferentes, donde el valor de la variable del argumento pasado es copiado al parámetro de la función al momento de invocarla, y a partir de ahí si se modifica el valor del parámetro, no afecta al valor de la variable original del programa principal.

Por último, la función print() de la línea 3 imprime un saludo personalizado con el nombre de la persona en pantalla.

¡Para recordar!

Siempre debes aplicar las buenas prácticas para definir los nombres de las variables y parámetros.

En este caso, hemos elegido el identificador nombre para el parámetro de la función, ya que almacenará el nombre de la persona a la que se dirige el saludo.

Veamos cómo se ejecuta el programa con esta modificación:

Terminal (Entrada/Salida)
¿Cuál es tu nombre? Pablo
Hola, Pablo!

Y así, hemos parametrizado nuestra subrutina hola() para que reciba un argumento y personalice el saludo con el nombre de la persona a la que se dirige el saludo. De esta manera podemos hacer más dinámico nuestro programa y personalizar el saludo según el nombre ingresado por el usuario, además de hacerlo en una sola línea.

Nota que nuestro código ya no tiene la última línea que imprime el nombre, ya que ahora la función hola() se encarga de imprimir el saludo personalizado con el nombre del usuario.

Esta optimización del código permite unificar el comportamiento de la subrutina, en este caso el saludo personalizado, y encapsular su lógica completa dentro de ella, para hacerla más flexible y reutilizable en diferentes contextos, simplemente pasando el argumento adecuado al momento de invocarla.

¡Para recordar!

Las subrutinas y funciones pueden recibir argumentos que se almacenan en parámetros y que se utilizan dentro de la subrutina o función para realizar su tarea.

De esta manera, se pueden personalizar las subrutinas y funciones para que realicen tareas específicas según los valores de los argumentos que se les pasen al momento de invocarlas.

Parametrización de subrutinas y funciones con valores por defecto

Ahora, ¿qué pasaría si quisiéramos que nuestra subrutina imprimiera un saludo generalizado cuando no se le pasa un nombre específico?

¿Podríamos configurar su comportamiento para que imprima un saludo a todo el mundo, de manera predeterminada, o que imprima un saludo personalizado cuando se le indique a quien saludar?

¡Para recordar!

En el caso de que no se pase un argumento al invocar una subrutina o función, es posible asignar un valor por defecto al parámetro que recibiría dicho argumento, durante su definición, para ser utilizado de manera predeterminada durante la ejecución de dicha subrutina o función.

Recuerdas que con print() había un valor predeterminado para sep, para el separador, y había un valor predeterminado para end, el final de línea. Nosotros también podemos recrear ese comportamiento con nuestro parámetros.

En nuestro caso, podríamos asignar un valor por defecto al parámetro nombre de la función hola(), para que si no se pasa un argumento al invocarla, se utilice ese valor por defecto:

Python
1
2
3
4
5
6
7
8
9
# Definiciones de funciones de usuario
def hola(nombre="Mundo"):
    print(f"Hola, {nombre}!")


# Código principal
hola()
nombre = input("¿Cuál es tu nombre? ")
hola(nombre)

En este caso, la función hola() de la línea 2 recibe un parámetro nombre con un valor por defecto "Mundo", que se utilizará si no se pasa un argumento al invocar la función.

En la línea 7, se invoca a la función hola() sin pasar un argumento, por lo que se utilizará el valor por defecto "Mundo" para personalizar el saludo.

Luego, en la línea 8, se solicita al usuario que ingrese su nombre y se almacena en la variable nombre.

Finalmente, en la línea 9, se invoca a la función hola() pasando como argumento el valor almacenado en la variable nombre, para personalizar el saludo con el nombre del usuario.

Veamos como se ejecuta el programa con esta modificación:

Terminal (Entrada/Salida)
Hola, Mundo!
¿Cuál es tu nombre? Pablo
Hola, Pablo!

Y así, hemos asignado un valor por defecto al parámetro nombre de la función hola(), para que si no se pasa un argumento al invocarla, se utilice ese valor por defecto. De esta manera, podemos personalizar el saludo con el nombre del usuario si se pasa un argumento, o con el valor por defecto si no se pasa un argumento.

¡Para recordar!

Es posible asignar valores por defecto a los parámetros de las subrutinas y funciones, para que se utilicen esos valores en caso de que no se pasen argumentos al invocarlas.

De esta manera, se pueden personalizar las subrutinas y funciones con valores por defecto que se utilizarán si no se pasan argumentos al invocarlas.

Convertir una subrutina en función

Hasta acá, habrás notado que la subrutina hola() solo tiene el llamado efecto secundario (side effect en inglés). Solo imprime algo en la pantalla.

Pero, ¿qué ocurriría quisiera que la subrutina no tenga un efecto secundario per se, si no que en realidad devuelva un resultado?

Python
1
2
3
4
5
6
7
8
9
# Definiciones de funciones de usuario
def hola(nombre="Mundo"):
    print(f"Hola, {nombre}!")


# Código principal
hola()
nombre = input("¿Cuál es tu nombre? ")
hola(nombre)

Desde el punto de vista de la optimización de código, es preferible que las funciones reciban argumentos y retornen valores, en lugar de depender de variables globales. Esto hace que el código sea más modular, más fácil de entender y de depurar, y más fácil de reutilizar en otros programas.

Si pensamos que la función input() devuelve la cadena de caracteres que el usuario escriba, que la función int() devuelve un dato convertido a entero, o que la función round() devuelve un valor redondeado, también podemos intuir que nuestras funciones podrían devolver un resultado.

La palabra clave return

Para que una función devuelva un valor, se utiliza la palabra clave return seguida del valor que se desea retornar.

Así que para convertir nuestra subrutina hola() en una función, simplemente debemos modificarla para que retorne el saludo como valor en lugar de imprimirlo.

Además, aprovecharemos para mejorar la lógica de la función, permitiendo que reciba un argumento que será el nombre de la persona a la que se le dirige el saludo:

Python
1
2
3
4
5
6
7
8
# Definiciones de funciones de usuario
def formatear_saludo(nombre="Mundo"):
    return f"Hola, {nombre}!"


# Código principal
nombre = input("¿Cuál es tu nombre? ")
print(formatear_saludo(nombre))

En este caso, la función formatear_saludo() recibe como argumento el valor almacenado en la variable nombre y retorna una cadena de caracteres que incluye el saludo concatenado al nombre pasado como argumento.

Terminal (Entrada/Salida)
¿Cuál es tu nombre? Pablo
Hola, Pablo!

Y así, hemos convertido nuestra subrutina hola() en una función formatear_saludo() que recibe un argumento y retorna un valor como resultado de su ejecución, un saludo personalizado con el nombre de la persona a la que se dirige el saludo.

¡Para recordar!

Al convertir una subrutina en una función, es importante tener en cuenta que la función debe retornar un valor como resultado de su ejecución, mientras que una subrutina no retorna nada.

Veamos otro ejemplo. Supongamos que queremos calcular el cuadrado de un número.

Python
1
2
3
4
5
6
7
# Función principal
def main():
    numero = int(input("Ingrese un número: "))
    print(f"El cuadrado de {numero} es {square(numero)}")

# Ejecución de la función principal
main()

Así como tenemos las funciones int(), float(), round(), etc. tamibién podríamos pensar que existe una función predefinida en Python que calcule el cuadrado de un número:

Terminal (Entrada/Salida)
Ingrese un número: 5 
Traceback (most recent call last):
  File "c:\Users\usuario\calculadora_potenciadora.py", line 7, in <module>
    main()
  File "c:\Users\usuario\calculadora_potenciadora.py", line 4, in main
    print(f"El cuadrado de {numero} es {square(numero)}")
                                        ^^^^^^
NameError: name 'square' is not defined

Pero no la hay. Así que recibimos un error de nombre (NameError en inglés) que nos indica que square() no está definida.

¿Qué podemos hacer? Podemos definir nuestra propia función que calcule el cuadrado de un número, y luego invocarla en nuestro programa principal.

Vamos a aprovechar para modificar el nombre de nuestra función por uno que esté en nuestro idioma y sea representativo de la tarea que realiza:

Python
# Función principal
def main():
    numero = int(input("Ingrese un número: "))
    print(f"El cuadrado de {numero} es {calcular_cuadrado(numero)}")

# Función para calcular el cuadrado de un número
def calcular_cuadrado(numero):
    return numero ** 2 # También se puede usar pow(numero, 2) o simplemente numero * numero

# Ejecución de la función principal
main()

La función que acabamos de definir, calcular_cuadrado(), recibirá un número y lo almacenará en el parámetro numero. Y luego utilizará el parámetro para calcular el cuadrado del valor almacenado en el y retornarlo al punto desde donde fuera llamada la función.

Si ejecutamos nuestro programa ahora, obtendremos la siguiente salida:

Terminal (Entrada/Salida)
Ingrese un número: 5
El cuadrado de 5 es 25

En este caso, la función calcular_cuadrado() recibe un argumento numero y retorna el cuadrado de ese número como resultado de su ejecución. Así es como ahora tenemos nuestra propia función que calcula el cuadrado de un número, que podemos invocar en cualquier parte de nuestro programa para realizar esa tarea específica las veces que sea necesario.

Buenas prácticas en el uso de funciones definidas por el usuario

¡Buena práctica sobre ordenamiento de código!

Cuando se ejecuta un programa de Python, lo que hace el intérprete del lenguaje es leer línea a línea el código de este, procesando toda sentencia que sea ejecutable.

Las funciones que sean declaradas en el programa las guarda en memoria para poder usarlas luego. Entonces es más ordenado y prolijo, primero, poner todas las funciones, y después el código "ejecutable" que invoque a estas funciones cuando sea necesario.

¡Buena práctica sobre convención de nombres!

Para nombres de funciones se suele emplear generalmente el tipo de casing snake_case, que es básicamente dejar todas las palabras en minúscula y unirlas con un guión bajo; además de emplear un nombre mnemotécnico, es decir que nos remita a la tarea que realizará esa función.

¡Buena práctica sobre definición del nombre!

Las funciones hacen "algo". Entonces sus nombres deben definirse como verbos en infinitivo (terminan en -ar, -er, -ir): calcular_suma, imprimir_mensaje, correr_prueba, obtener_triplicado, etc.

Las excepciones son las funciones que devuelven un valor booleano (aquellas que devuelven uno de dos posibles valores: verdadero o falso) que podrían llamarse como: es_par, da_cero, tiene_letra_a, porque devuelven True o False, confirmando o negando la afirmación que referencia el nombre.

Referencias:


  1. Una subrutina en Python es un bloque de código que realiza una tarea específica y puede ser llamada y ejecutada desde otros lugares en el programa.
    En Python, las subrutinas se implementan de igual manera que las funciones.
    La diferencia principal entre una función y una subrutina es que una función retorna (return en inglés) un valor (resultado), mientras que una subrutina no devuelve nada.