6. Las funciones

6.1. Script [fonc_01]: ámbito de las variables
El script [fonc_01] muestra ejemplos del alcance de las variables entre funciones:
# alcance de las variables
def f1():
# Se debe evitar el uso de variables globales
# variable global i
global i
i += 1
# variable local j
j = 10
print(f"f1[i,j]=[{i},{j}]")
def f2():
# Se debe evitar el uso de variables globales
# variable global i
global i
i += 1
# variable local j
j = 20
print(f"f2[i,j]=[{i},{j}]")
def f3():
# variable local i
i = 1
# variable local j
j = 30
print(f"f3[i,j]=[{i},{j}]")
# programa principal
i = 0
j = 0
# estas dos variables solo serán conocidas por una función f
# a menos que esta declare explícitamente, mediante la instrucción global, que desea utilizarlas
# o si la función utiliza la variable global solo en modo de lectura
f1()
f2()
f3()
# j no ha cambiado, pero i sí ha cambiado
print(f"[i,j]=[{i},{j}]")
Resultados
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_01.py
f1[i,j]=[1,10]
f2[i,j]=[2,20]
f3[i,j]=[1,30]
[i,j]=[2,0]
Process finished with exit code 0
Notas:
- el script muestra el uso de la variable i, declarada como global en las funciones f1 y f2. En este caso, el programa principal y las funciones f1 y f2 comparten la misma variable i.
6.2. Script [fonc_02]: ámbito de las variables
El script [fonc_03] retoma el script [fonc_02] y muestra cómo evitar el uso de variables globales:
# alcance de las variables
def f1(i):
# variable local i
i += 1
# variable local j
j = 10
print(f"f1[i,j]=[{i},{j}]")
# se devuelve el valor modificado
return i
def f2(i):
# variable local i
i += 1
# variable local j
j = 20
print(f"f2[i,j]=[{i},{j}]")
# se devuelve el valor modificado
return i
def f3():
# variable local i
i = 1
# variable local j
j = 30
print(f"f3[i,j]=[{i},{j}]")
# programa principal
i = 0
j = 0
# estas dos variables solo serán conocidas por una función f
# solo si esta declara explícitamente, mediante la instrucción global, que desea utilizarlas
# o si la función utiliza la variable global solo en modo de lectura
i = f1(i)
i = f2(i)
f3()
# j no ha cambiado, pero i sí ha cambiado
print(f"[i,j]=[{i},{j}]")
Comentarios:
- líneas 2, 12: en lugar de declararse como global, la variable [i] se pasa como parámetro a las funciones f1 y f2;
- líneas 9, 19: las funciones f1 y f2 devuelven la variable [i] modificada al programa principal. Este la recupera en las líneas 36 y 37;
Resultados
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_02.py
f1[i,j]=[1,10]
f2[i,j]=[2,20]
f3[i,j]=[1,30]
[i,j]=[2,0]
Process finished with exit code 0
6.3. Script [fonc_03]: ámbito de las variables
El script [fonc_03] muestra una particularidad de las variables utilizadas tanto en una función como en el código que la llama, dependiendo de si en la función dicha variable se utiliza únicamente en modo de lectura o no.
def f1():
# aquí se conoce la variable global i
print(f"[f1] i={i}")
# aquí se conoce la variable global j
print(f"[f1] j={j}")
def f2():
# aquí la variable global i no se conoce
# porque la función f2 define una variable local con el mismo nombre
# por lo que es esta la que tiene prioridad
try:
# intento de mostrar la variable local i definida más adelante
print(f"[f2] i={i}")
# la instrucción siguiente convierte a i en una variable local de la función f2
i = 7
except BaseException as erreur:
print(f"[f2] erreur={erreur}")
def f3():
# aquí no se conoce la variable global i
# ya que la función f3 define una variable local con el mismo nombre
# por lo que es esta la que tiene prioridad
# la instrucción siguiente convierte a i en una variable local
i = 7
# visualización: aquí i es conocida
print(f"[f3] i={i}")
# main -----------
# variables globales de las funciones
i = 10
j = 20
# llamada a f1
f1()
print(f"[main] i={i}, j={j}")
# llamada a f2
f2()
print(f"[main] i={i}")
# llamada a f3
f3()
print(f"[main] i={i}")
Notas
- línea 34: el código principal define una variable [i];
- líneas 1-5: la función f1 también utiliza una variable [i] sin asignarle un valor. Se trata de una lectura de la variable [i]. En este caso, la variable [i] utilizada es la del código que la llama, línea 34;
- líneas 8-18: la función f2 también utiliza una variable [i], pero le asigna un valor en la línea 16. Al asignar un valor a la variable [i] en f2, [i] se convierte automáticamente en una variable local de la función [f2]. Por lo tanto, esta «oculta» la variable [i] del código que la llama;
- línea 14: la operación de escritura de la variable local [i] fallará porque esta no tiene valor al llegar a la línea 14. Obtiene su valor en la línea 16. Se producirá una excepción. Por esta razón, se ha insertado la línea 14 dentro de un try / catch;
- líneas 21-29: la función f3 hace lo mismo que la función f2, pero define antes su variable local [i];
Resultados
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_03.py
[f1] i=10
[f1] j=20
[main] i=10, j=20
[f2] erreur=local variable 'i' referenced before assignment
[main] i=10
[f3] i=7
[main] i=10
Process finished with exit code 0
6.4. Script [fonc_04]: modo de paso de parámetros
El script es el siguiente:
# función f1
def f1(a):
a = 2
# función f2
def f2(a, b):
a = 2
b = 3
return a, b
# ------------------------ main
x = 1
f1(x)
print(f"x={x}")
(x, y) = (-1, -1)
(x, y) = f2(x, y)
print(f"x={x}, y={y}")
Resultados
Notas:
- todo es un objeto en Python. Algunos objetos se denominan «inmutables» (en inglés): no se pueden modificar. Este es el caso de los números, las cadenas de caracteres y las tuplas. Cuando los objetos de Python se pasan como parámetros a funciones, lo que se pasa son sus referencias, a menos que dichos objetos sean «inmutables», en cuyo caso se pasa el valor del objeto;
- las funciones f1 (línea 2) y f2 (línea 7) pretenden ilustrar el paso de un parámetro de salida. Se busca que el parámetro efectivo de una función sea modificado por la función;
- líneas 2-3: la función f1 modifica su parámetro formal a. Queremos saber si el parámetro efectivo también se modificará;
- líneas 14-15: el parámetro efectivo es x=1. La línea 2 de los resultados muestra que el parámetro efectivo no se modifica. Por lo tanto, el parámetro efectivo x y el parámetro formal a son dos objetos diferentes;
- líneas 8-10: la función f2 modifica sus parámetros formales a y b, y los devuelve como resultados;
- líneas 17-18: se pasan a f2 los parámetros efectivos (x, y) y el resultado de f2 se asigna a (x, y). La línea 3 de los resultados muestra que los parámetros efectivos (x, y) han sido modificados.
De ello se deduce que, cuando los objetos «inmutables» son parámetros de salida, deben formar parte de los resultados devueltos por la función.
6.5. Script [fonc_05]: orden de escritura de las funciones en un script
El script [fonc_05] muestra que no se puede llamar a una función si no se ha encontrado previamente en el código:
# ------------------------ main
print(f2(100, 200))
# función f1
def f1(a):
return a + 10
# función f2
def f2(a, b):
return f1(a + b)
Notas
- la línea 2 provocará un error porque utiliza la función f2, que aún no se ha definido en el script;
Resultados
6.6. Script [fonc_06]: orden de escritura de las funciones en un script
El script [fonc_06] muestra que lo que se aplica al código que realiza la llamada no se aplica a las funciones:
# función f2
def f2(a, b):
return f1(a + b)
# función f1
def f1(a):
return a + 10
# ------------------------ main
print(f2(100, 200))
Notas
- línea 3: la función [f2] utiliza la función [f1] definida más adelante en el script. Sin embargo, esto no genera ningún error. Por lo tanto, se puede concluir que el orden de definición de las funciones en un script de Python no tiene importancia;
Resultados
6.7. Script [fonc_07]: uso de módulos
El script [fonc_07] muestra cómo aislar las funciones en un módulo.

Aislamos en un módulo las funciones reutilizables. En lugar de trasladarlas de un script a otro:
- las colocamos en un archivo aparte que declaramos de una manera específica;
- los scripts que necesitan estas funciones «importan» el módulo que las contiene;
El script [fonctions_module_01] es el siguiente:
# función f2
def f2(a, b):
return f1(a + b)
# función f1
def f1(a):
return a + 10
Para que otros scripts puedan hacer referencia a las funciones del script [fonctions_module_01], existen diferentes formas de hacerlo. Estas varían dependiendo de si el script se ejecuta o no dentro de [PyCharm].
En [PyCharm], los módulos importados se buscan en carpetas específicas llamadas [Sources Root]. Hay dos formas de convertir una carpeta en un [Sources Root]:

- en [4], la carpeta ha cambiado de color;
Tras esta operación, la carpeta [fonctions/modules] se reconoce como una carpeta de origen. Entonces se puede escribir en un script:
Para importar o utilizar la función f2 definida en el módulo [fonctions_module_01.py].
![]() |
Otro método consiste en utilizar las propiedades del proyecto:
- en el ejemplo anterior, la secuencia [1-6] permite convertir la carpeta [shared] en una carpeta donde almacenar los módulos que se van a importar;
Por el momento, no vamos a declarar ninguna carpeta como [Sources Root], aparte de la raíz del proyecto:

Una vez hecho esto, podemos escribir el siguiente script [fonc-07]:
# uso de módulos
import sys
# ------------------------ principal
print(f"Python path={sys.path}")
from fonctions.shared.fonctions_module_01 import f2
print(f2(100, 200))
- línea 2: se importa el objeto [sys] para poder utilizar, en la línea 5, su atributo [path], que proporciona lo que se conoce como [Python Path]: una lista de carpetas que se explorarán en busca de módulos importados;
- línea 6: se importa la función f2 del módulo [fonctions_module_01]. Para designar este módulo, se utiliza la ruta que va desde la raíz del proyecto hasta el módulo. Con PyCharm, la raíz del proyecto siempre forma parte de las carpetas exploradas cuando se busca un módulo importado en un script. Por lo tanto, esta carpeta forma parte del [Python Path] del proyecto. Esto es lo que la línea 5 nos permitirá verificar;
- línea 6: si describiéramos la ruta que va desde la raíz del proyecto hasta la carpeta [fonctions_module_01], escribiríamos [fonctions/shared/fonctions_module_01]. En la ruta de un módulo, el signo / se reemplaza por el punto. Por lo tanto, escribimos [fonctions.modules.fonctions_module_01];
- después de la línea 6, ya se conoce la función f2. Se utiliza en la línea 8;
Resultados
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_07.py
Python path=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions\\shared', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages']
310
Process finished with exit code 0
Arriba:
- resaltado en verde, se ve que la raíz del proyecto forma parte de [Python Path];
- resaltado en amarillo, se ve que la carpeta del script ejecutado también forma parte de [Python Path];
- los demás elementos del [Python Path] provienen directamente de la carpeta de instalación de Python;
¿Qué sucede cuando no se utiliza PyCharm para ejecutar [fonc-07]?


- En [1], se ejecuta el script [fonc-07]. Nos encontramos en la carpeta [fonctions];
- en [2], vemos que la carpeta de ejecución forma parte de [Python Path]. Siempre es así. También podemos ver que la carpeta raíz [C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020] no forma parte de [Python Path];
- en [3], el intérprete de Python indica que no encuentra el módulo [fonctions];
Para buscar el módulo importado [fonctions.shared.fonctions_module_01], el intérprete de Python busca en las carpetas de [Python Path] una subcarpeta llamada [fonctions]. No lo encuentra por ningún lado. De hecho, la subcarpeta [fonctions] se encuentra dentro de la carpeta [C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020], la cual no forma parte de [Python Path].
El script [fonc-08] ofrece una posible solución a este problema.
6.8. Script [fonc_08]: agregar carpetas a [Python Path]
Es posible modificar el [Python Path] mediante programación, tal como lo muestra el script [fonc-08]:
# uso de módulos
import os
import sys
# carpeta del script
script_dir = os.path.dirname(os.path.abspath(__file__))
# Ruta de Python antes de la modificación
print(f"Python path avant={sys.path}")
# se agrega la carpeta [shared] a la ruta de Python
sys.path.append(f"{script_dir}/shared")
# Ruta de Python después de la modificación
print(f"Python path après={sys.path}")
# import f2
from fonctions_module_01 import f2
# ------------------------ main
print(f2(100, 200))
Notas
- línea 4: la variable especial [__file__] es el nombre del script que se ejecuta. Dependiendo del contexto de ejecución, este nombre puede ser absoluto (Pycharm) o relativo (consola). La función [os.path.abspath] devuelve el nombre absoluto del archivo cuyo nombre se le pasa como parámetro. La función [os.path.dirname] devuelve el nombre absoluto de la carpeta que contiene el archivo cuyo nombre se le pasa como parámetro;
- línea 10: [sys.path] es una matriz que contiene los nombres de las carpetas que se deben explorar cuando se busca un módulo. A esta matriz se le agrega la raíz del proyecto definida en la línea 4;
- se muestra el [Python Path] antes (línea 8) y después (línea 12) de la modificación;
- línea 15: se importa el módulo [fonctions_module_01], que contiene la función f2;
La ejecución en PyCharm arroja los siguientes resultados:
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_08.py
Python path avant=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions\\shared', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages']
Python path après=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions\\shared', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions/shared']
310
Process finished with exit code 0
- línea 3: se observa que el archivo [shared] aparece dos veces en el [Python Path]. Esto se puede evitar, pero en este caso no supone ningún problema;
- línea 4: la función f2 se ha ejecutado correctamente;
Ahora ejecutemos [fonc-08] en un terminal:
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\fonctions>python fonc_08.py
Python path avant=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages']
Python path après=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions/shared']
310
- línea 2: al igual que antes, el archivo [shared] no se encuentra en el [Python Path];
- línea 3: ahora sí está ahí;
- línea 4: se ha encontrado la función f2;
6.9. Script [fonc_09]: declaración del tipo de los parámetros
El script [fonc_09] muestra que se puede declarar el tipo de los parámetros de una función, así como el del resultado. Sin embargo, esta declaración solo es útil para la documentación de la función. El intérprete de Python no verifica que los parámetros efectivos de la función tengan realmente el tipo esperado. Sin embargo, PyCharm señala las inconsistencias de tipos entre los parámetros efectivos y los formales. Esta sola razón basta para que las declaraciones de tipo sean indispensables.
El script es el siguiente:
# una función con indicación del tipo de los parámetros
# Esto sirve únicamente como documentación, ya que el intérprete de Python no lo toma en cuenta
def show(param: int) -> int:
print(f"param={param}, type(param)={type(param)}")
return param + 1
# main -------------------------
print(show(4))
show("xyz")
Notas:
- línea 5: se declara que el parámetro formal [param] es de tipo [int] y que el resultado de la función también es de tipo [int];
- línea 11: el parámetro efectivo de la función [show] tiene el tipo correcto;
- línea 12: el parámetro efectivo de la función [show] no tiene el tipo correcto;
Resultados
- línea 10: el tipo del parámetro [param] es de tipo [str]. Cuando aparece este mensaje, ya se ha ingresado al código de la función [show]. Por lo tanto, el intérprete de Python aceptó que el parámetro efectivo de la función [show] fuera de tipo [str];
- la línea 7 del código provoca la excepción reflejada en las líneas 4 a 10 de los resultados;
Sin embargo, PyCharm indica que hay una anomalía:

En [1], PyCharm ha resaltado la llamada errónea.
6.10. Script [fonc_10]: parámetros con nombre
Para pasar parámetros a una función, se pueden utilizar los nombres de los parámetros formales de esta. En este caso, no es necesario respetar el orden de los parámetros formales:
# se pueden designar los parámetros efectivos por sus nombres formales
def f(x, y):
return x + y
# main
print(f(y=10, x=3))
Notas
- línea 2: la función f tiene dos parámetros formales, x e y;
- línea 7: al llamar a la función f, se pueden utilizar los nombres de los parámetros formales. Esta práctica puede ser útil al menos en dos casos:
- la función tiene muchos parámetros, la mayoría de los cuales tienen un valor por defecto. Al llamarla, la técnica anterior permite inicializar únicamente los parámetros para los que no se desea utilizar el valor por defecto;
- si los parámetros formales tienen un nombre significativo, entonces el uso de parámetros con nombre en la llamada a la función mejora la legibilidad del código;
Resultados
6.11. Script [fonc_11]: función recursiva
El script [fonc_11] es un ejemplo de función recursiva (que se llama a sí misma):
# función recursiva
def fact(i: int) -> int:
# factorial(1) es 1
# una función recursiva debe terminar en algún momento
if i == 1:
return 1
else:
# factorial(i) = i * factorial(i-1)
return i * fact(i - 1)
# ---------- main
print(f"fact(8)={fact(8)}")
Comentarios
- líneas 1-9: la función factorial;
- línea 9: la función [factorielle] se llama a sí misma;
- líneas 5-6: una función recursiva siempre debe detenerse cuando se cumple una condición; de lo contrario, se produce una recursión infinita;
Resultados
6.12. Script [fonc_12]: función recursiva
La función [fonc_12] ofrece más detalles sobre el funcionamiento de la recursividad:
# función recursiva
# comportamiento del parámetro j
def fact(i: int, j: int) -> int:
# fin de la función recursiva
if i == 1:
print(f"j={j}")
return 1
else:
# se manipula j
j += 1
print(f"avant fact j={j}")
# recursividad
f = fact(i - 1, j)
print(f"après fact j={j}")
# resultado
return i * f
# ---------- main
print(f"fact(8)={fact(8, 0)}")
Comentarios
- línea 5: seguimos trabajando con la función factorial. Le agregamos el parámetro [j];
- línea 12: la variable j se incrementa regularmente con cada factorial. Se muestra su valor antes (línea 12) y después (línea 16) de la recursividad (línea 15);
Resultados
- líneas 2-8: se observa que el valor de [j] aumenta mientras la recursividad continúa, hasta que se cumple la condición en la que la recursividad se detiene. A partir de ese momento, el retorno de las llamadas a la función [fact] se realiza en orden inverso al de las llamadas;
- líneas 10-16: estas salidas reflejan los retornos sucesivos de la llamada a la factorial. La variable [j] recupera sus valores hasta llegar a su valor inicial 1;
