Código de ejemplo de cierres de Python

Estoy aprendiendo Python usando el libro Dive Into Python 3 . Me gusta, pero no entiendo el ejemplo usado para introducir los cierres en la Sección 6.5.

Quiero decir, veo cómo funciona, y creo que es realmente genial. Pero no veo ningún beneficio real: me parece que se puede lograr el mismo resultado simplemente leyendo el archivo de reglas línea por línea en un bucle y haciendo una búsqueda / reemplazo para cada línea leída.

¿Podría alguien ayudarme a:

¡Gracias!

Los decoradores son un ejemplo de cierres. Por ejemplo,

def decorate(f): def wrapped_function(): print("Function is being called") f() print("Function call is finished") return wrapped_function @decorate def my_function(): print("Hello world") my_function() 

La wrapped_function es un cierre, porque retiene el acceso a las variables en su scope, en particular, el parámetro f, la función original. Los cierres son los que te permiten acceder a él.

Los cierres también le permiten retener el estado en todas las llamadas de una función, sin tener que recurrir a una clase:

 def make_counter(): next_value = 0 def return_next_value(): nonlocal next_value val = next_value next_value += 1 return val return return_next_value my_first_counter = make_counter() my_second_counter = make_counter() print(my_first_counter()) print(my_second_counter()) print(my_first_counter()) print(my_second_counter()) print(my_first_counter()) print(my_second_counter()) 

Además, los métodos enlazados son cierres técnicos (aunque probablemente se implementen de manera diferente). Los métodos enlazados son funciones de miembro de clase con su clase al horno en:

 import sys w = sys.stdout.write w("Hello\n") 

w es esencialmente un cierre con una referencia al objeto sys.stdout .

Finalmente, no he leído ese libro, pero una lectura rápida del capítulo que vinculaste y no estoy muy impresionado, es tan horrible que es inútil como explicación de los cierres.

Esto puede no parecer particularmente útil cuando tiene acceso a la base del código completo o cuando no tiene en mente la capacidad de reutilización, pero es increíblemente potente y útil cuando se trata de separar la lógica en módulos diferentes y reutilizables que se pueden implementar en paralelo mediante diferentes desarrolladores Si simplemente leyera las cadenas de patrones del archivo, todos los módulos tendrían que conocer este archivo y pasar esa molesta lista de cadenas de patrones. Y si cambiaste el sistema para que las cadenas de patrones provinieran de una URL en lugar de un archivo, podría romper completamente tu base de código. Por otro lado, si la lógica de procesamiento simplemente toma una función de callback o varias funciones de callback, y luego tiene otro módulo que construye las funciones dinámicamente utilizando el contenido de un archivo, entonces solo el componente que construye las funciones debe cambiar. Ese es el poder de poder crear funciones dinámicamente.

He aquí un uso de cierre, de get config:

 def confmaker(): cf=ini_conf() def clo(*args): return cf.get(*args) return clo cfget=confmaker() cfget(...) 

Aquí se llama ini_conf solo una vez. En mi entendimiento, los cierres evitan las variables globales (como cf), y hacen que el uso sea simple.

Niels-Bom escribe (con ediciones):

El mismo resultado podría lograrse simplemente leyendo en línea el archivo de reglas en un bucle y haciendo una búsqueda / reemplazo para cada línea leída.

Y, de hecho, esto es lo que esencialmente se logra en la sección 6.5, donde las reglas se colocan dentro del archivo plural4-rules.txt. Con las reglas ahora como cadenas se pueden mantener dentro de un archivo, y nuestro código separa los datos del control. Esto hace que el proyecto sea más fácil de gestionar y mantener.

La pregunta de Niels me motivó a esbozar el desarrollo del capítulo 6 en un esfuerzo por entender exactamente lo que el autor estaba tratando de demostrar. Hay muchas lecciones que aprender sobre el desarrollo proporcionado y no se trata solo de cierres, sino también de las mejores prácticas en encoding.

El ejercicio me ayudó a comprender cómo se pueden usar los generadores para reemplazar implementaciones alternativas, menos abstractas y más enredadas. El desarrollo del material de 6.2 a 6.6 es lo suficientemente educativo como para bosquejarlo aquí.

Así que empiezo con el segundo punto de Niels sobre la división de las reglas en funciones separadas en 6.3 como un segmento en el bosquejo:

¿Por qué el uso de cierres en este ejemplo mejora el código?

El autor afirma en este punto:

¿Valía la pena agregar este nivel de abstracción? Bueno, todavía no.

Todavía hay secciones 6.4-6.6 para trabajar. La historia de los cierres, y en este caso, la construcción de un generador de pluralización se logra paso a paso, comenzando con las reglas codificadas en un módulo llamado plural (sustantivo). Entonces, comenzando con la primera sección relevante y resumiendo nuestro camino hasta el final del capítulo, tenemos lo siguiente.

6.2 Usemos expresiones regulares: aquí el autor aprovecha la oportunidad para reforzar y ampliar nuestra comprensión de las expresiones regulares con reglas de pluralización codificadas dentro de la función plural inicial.

6.3. Una lista de funciones: resume las reglas codificadas dentro de la función plural a varias funciones independientes. Este es un “escalón” a la siguiente sección. Pero también demuestra que hay una diferencia importante entre el uso de match_sxz () y match_sxz.

6.4 Una lista de patrones: El hecho de que creamos funciones nombradas individuales, emparejadas como coincidencia y aplicación, en 6.3 son redundantes. Todas estas funciones se basan en el mismo patrón y nunca se llaman directamente. Aquí modifica este código para simplificar el cambio de reglas. Esto se convierte en un nivel adicional de abstracción, con las reglas ahora especificadas como cadenas dentro de la variable llamada patrón. Las reglas de pluralización ya no son funciones.

6.5 Un archivo de patrones: Sin más códigos duplicados y las reglas de pluralización definidas en una lista de cadenas, el siguiente paso para construir el generador es colocar estas cadenas en un archivo separado. Aquí se vuelven más mantenibles, separados del código que los utiliza.

6.6 Generadores: El generador es una función plural () genérica que analiza el archivo de reglas, comprueba si hay una coincidencia, aplica la regla si corresponde y pasa a la siguiente regla. Este es un ejemplo de un cierre.

Eso es todo lo que tiene que hacer la función plural (), y eso es todo lo que debe hacer la función plural ().

Un desarrollo relativamente simple y hermoso, lo suficientemente sofisticado como para ser útil y extensible a otros tipos de problemas que uno pueda encontrar, especialmente en el reconocimiento de patrones de texto.

El autor aborda los problemas de rendimiento de esta solución en particular al final del tutorial. Abrir y leer líneas de un archivo degradará el rendimiento, especialmente con un número creciente de llamadas open (). Afirma que se puede lograr un mejor rendimiento utilizando un iterador, que se considera más adelante en el libro.

Leyendo en el archivo de reglas línea por línea en un bucle

línea por línea en un bucle

lazo

Esto impulsará el rendimiento a través del suelo. Leer una vez, aplicar muchas veces.