Los módulos (y paquetes) son una excelente forma Pythonic de dividir su programa en espacios de nombres separados, lo que parece ser un objetivo implícito de esta pregunta. De hecho, mientras estaba aprendiendo los conceptos básicos de Python, me sentí frustrado por la falta de una función de alcance de bloque. Sin embargo, una vez que entendí los módulos de Python, pude realizar mis objetivos anteriores de manera más elegante sin la necesidad de un alcance de bloque.
Como motivación, y para señalar a las personas en la dirección correcta, creo que es útil proporcionar ejemplos explícitos de algunas de las construcciones de alcance de Python. Primero explico mi intento fallido de usar clases de Python para implementar el alcance del bloque. A continuación, explico cómo logré algo más útil usando módulos de Python. Al final, describo una aplicación práctica de paquetes para cargar y filtrar datos.
Intentando bloquear el alcance con clases
Por unos momentos pensé que había logrado el alcance del bloque al pegar código dentro de una declaración de clase:
x = 5
class BlockScopeAttempt:
x = 10
print(x)
print(x)
Desafortunadamente, esto se rompe cuando se define una función:
x = 5
class BlockScopeAttempt:
x = 10
print(x)
def printx2():
print(x)
printx2()
Eso es porque las funciones definidas dentro de una clase usan un alcance global. La forma más fácil (aunque no la única) de solucionar esto es especificar explícitamente la clase:
x = 5
class BlockScopeAttempt:
x = 10
print(x)
def printx2():
print(BlockScopeAttempt.x)
printx2()
Esto no es tan elegante porque uno debe escribir funciones de manera diferente dependiendo de si están o no incluidas en una clase.
Mejores resultados con los módulos de Python
Los módulos son muy similares a las clases estáticas, pero los módulos son mucho más limpios en mi experiencia. Para hacer lo mismo con los módulos, hago un archivo llamado my_module.py
en el directorio de trabajo actual con el siguiente contenido:
x = 10
print(x)
def printx():
global x
print(x)
Luego, en mi archivo principal o sesión interactiva (por ejemplo, Jupyter), hago
x = 5
import my_module
my_module.printx()
print(x)
Como explicación, cada archivo de Python define un módulo que tiene su propio espacio de nombres global. Importar un módulo le permite acceder a las variables en este espacio de nombres con la .
sintaxis.
Si está trabajando con módulos en una sesión interactiva, puede ejecutar estas dos líneas al principio
%load_ext autoreload
%autoreload 2
y los módulos se recargarán automáticamente cuando se modifiquen sus archivos correspondientes.
Paquetes para cargar y filtrar datos
La idea de paquetes es una pequeña extensión del concepto de módulos. Un paquete es un directorio que contiene un __init__.py
archivo (posiblemente en blanco) , que se ejecuta al importar. Se puede acceder a los módulos / paquetes dentro de este directorio con la .
sintaxis.
Para el análisis de datos, a menudo necesito leer un archivo de datos grande y luego aplicar varios filtros de forma interactiva. Leer un archivo lleva varios minutos, por lo que solo quiero hacerlo una vez. Basado en lo que aprendí en la escuela sobre programación orientada a objetos, solía creer que uno debería escribir el código para filtrar y cargar como métodos en una clase. Una gran desventaja de este enfoque es que si luego vuelvo a definir mis filtros, la definición de mi clase cambia, por lo que tengo que recargar toda la clase, incluidos los datos.
Hoy en día con Python, defino un paquete llamado my_data
que contiene submódulos llamados load
y filter
. Dentro de filter.py
puedo hacer una importación relativa:
from .load import raw_data
Si modifico filter.py
, autoreload
detectaré los cambios. No se recarga load.py
, así que no necesito recargar mis datos. De esta manera puedo crear un prototipo de mi código de filtrado en un cuaderno de Jupyter, ajustarlo como una función y luego cortar y pegar desde mi cuaderno directamente en filter.py
. Descubrir esto revolucionó mi flujo de trabajo y me convirtió de un escéptico a un creyente en el "Zen de Python".
One purpose (of many) is to improve code readability
- El código Python, escrito correctamente (es decir, siguiendo el zen de Python ) no necesitaría tal guarnición para ser legible. De hecho, es una de las (muchas) cosas que me gustan de Python.