Cuando codifica en otros lenguajes, a veces creará un alcance de bloque, como este:
statement
...
statement
{
statement
...
statement
}
statement
...
statement
Uno de los propósitos (de muchos) es mejorar la legibilidad del código: mostrar que ciertas declaraciones forman una unidad lógica o que ciertas variables locales se usan solo en ese bloque.
¿Existe una forma idiomática de hacer lo mismo en 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.__exit__
ywith
declaración, cambiando elglobals()
pero fallado.Respuestas:
No, no hay soporte de idioma para crear un alcance de bloque.
Las siguientes construcciones crean alcance:
fuente
La forma idiomática en Python es mantener sus funciones cortas. Si cree que lo necesita, ¡refactorice su código! :)
Python crea un nuevo alcance para cada módulo, clase, función, expresión generadora, comprensión de dictados, comprensión de conjuntos y en Python 3.x también para cada comprensión de lista. Aparte de estos, no hay ámbitos anidados dentro de las funciones.
fuente
Puede hacer algo similar al alcance de un bloque de C ++ en Python declarando una función dentro de su función y luego llamándola inmediatamente. Por ejemplo:
def my_func(): shared_variable = calculate_thing() def do_first_thing(): ... = shared_variable do_first_thing() def do_second_thing(): foo(shared_variable) ... do_second_thing()
Si no está seguro de por qué podría querer hacer esto, este video podría convencerlo.
El principio básico es abarcar todo lo más estrictamente posible sin introducir ninguna 'basura' (tipos / funciones adicionales) en un alcance más amplio de lo que se requiere absolutamente.Nada más quiere usar el
do_first_thing()
método, por ejemplo, por lo que no debe tener un alcance fuera del función de llamada.fuente
Estoy de acuerdo en que no hay alcance de bloque. Pero un lugar en Python 3 lo hace PARECER como si tuviera un alcance de bloque.
¿Qué pasó lo que le dio esta mirada? Esto funcionaba correctamente en Python 2. pero para hacer que la fuga de variables se detenga en Python 3, han hecho este truco y este cambio hace que parezca que tiene un alcance de bloque aquí.
Dejame explicar.
Según la idea de alcance, cuando introducimos variables con los mismos nombres dentro del mismo alcance, su valor debe modificarse.
esto es lo que está sucediendo en Python 2
>>> x = 'OLD' >>> sample = [x for x in 'NEW'] >>> x 'W'
Pero en Python 3, aunque se introduce la variable con el mismo nombre, no se anula, la comprensión de la lista actúa como una caja de arena por alguna razón y parece crear un nuevo alcance en ella.
>>> x = 'OLD' >>> sample = [x for x in 'NEW'] >>> x 'OLD'
y esta respuesta va en contra de la declaración del answerer @ Thomas El único medio para crear un alcance son funciones, clases o módulos porque esto parece otro lugar para crear un nuevo alcance.
fuente
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) # Output: 10 print(x) # Output: 5
Desafortunadamente, esto se rompe cuando se define una función:
x = 5 class BlockScopeAttempt: x = 10 print(x) # Output: 10 def printx2(): print(x) printx2() # Output: 5!!!
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) # Output: 10 def printx2(): print(BlockScopeAttempt.x) # Added class name printx2() # Output: 10
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) # (A) def printx(): global x print(x) # (B)
Luego, en mi archivo principal o sesión interactiva (por ejemplo, Jupyter), hago
x = 5 import my_module # Output: 10 from (A) my_module.printx() # Output: 10 from (B) print(x) # Output: 5
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 llamadosload
yfilter
. Dentro defilter.py
puedo hacer una importación relativa:from .load import raw_data
Si modifico
filter.py
,autoreload
detectaré los cambios. No se recargaload.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 enfilter.py
. Descubrir esto revolucionó mi flujo de trabajo y me convirtió de un escéptico a un creyente en el "Zen de Python".fuente