2010-05-09 26 views
13

Estoy aprendiendo Python usando el libro Dive Into Python 3. Me gusta, pero no entiendo el example used to introduce Closures en la Sección 6.5.Código de ejemplo de Python Closures

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 en el archivo de reglas línea por línea en un bucle, y haciendo búsqueda/reemplazo para cada lectura de línea.

Podría alguien ayudarme a:

  • bien entender por qué el uso de cierres en este ejemplo mejora del código (por ejemplo, más fácil de mantener, ampliar, reutilización o depuración?)

  • o sugerir ¿una fuente de otros ejemplos de código de la vida real donde realmente brillan los cierres?

¡Gracias!

Respuesta

21

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 función wrapped_function es un cierre, ya que mantiene el acceso a las variables en su ámbito de aplicación - en particular, el parámetro f, la función original. Los cierres son los que le permiten acceder a él.

Los cierres también le permiten conservar el estado a través de 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 consolidados son técnicamente cierres (aunque es probable que estén implementados de manera diferente). Métodos vinculados son funciones miembro de la clase con su clase horneados 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 vinculó y estoy muy poco impresionado - es tan horriblemente indirecto que es inútil como una explicación de los cierres.

+0

¡Gracias! Me encantaría usar un libro de Python 3 que sea similar en estilo y profundidad a esta respuesta, pero no pude encontrar uno ... – max

2

Esto puede no parecer particularmente útil cuando tiene acceso a toda la base de códigos o cuando no tiene en mente la reutilización, pero es increíblemente potente y útil al tratar de separar la lógica en diferentes módulos reutilizables que pueden ser implementado en paralelo por diferentes desarrolladores. Si tuviera que leer simplemente las cadenas de patrones del archivo, cada módulo debería tener en cuenta este archivo y pasar esa molesta lista de cadenas de patrones. Y si modificó su sistema para que las cadenas de patrones provinieran de una URL en lugar de un archivo, podría romper por completo su base de código. Por otro lado, si procesa lógica simplemente toma una función de devolución de llamada o varias funciones de devolución de llamada, y luego tiene otro módulo que construye las funciones de forma dinámica utilizando contenido de un archivo, entonces solo el componente que construye las funciones debe cambiar. Ese es el poder de poder crear funciones dinámicamente.

+0

Sí; pero ¿por qué no pasar solo la lista de cadenas? Un módulo se ocupará del archivo o URL o de cualquier otra fuente de entrada que tengamos. Y el resto del programa tomará las reglas reales como una lista de cadenas de patrones. – max

+0

@ user336527, el otro módulo simplemente puede verificar que un objeto cumple una lista de predicados. Ni siquiera necesita saber cuáles son esos predicados. Esto simplifica la tarea del segundo módulo y permite su reutilización con predicados no relacionados con la coincidencia de cadenas. Aunque, estaré de acuerdo, el ejemplo es un tanto artificial. Hay mejores casos de uso que el ejemplo. –

+0

¡Gracias, esto me aclara las cosas! – max

0

la lectura en la línea de archivo de reglas de línea en un bucle

línea por línea en un bucle

bucle

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

+0

Lo siento, no estaba siendo muy preciso. Por supuesto, almacenaría las cadenas de patrones que leí del archivo en una lista, y luego siempre recorreré esa lista. Pero, ¿cómo es mejor pasar una lista de funciones que pasar una lista de cadenas de patrones? – max

2

aquí es un uso de cierre, de conseguir configura:

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

cfget=confmaker() 

cfget(...) 

aquí ini_conf se llama sólo una vez. Según entiendo, los cierres evitan variables globales (como cf) y simplifican el uso.

1

Niels-Bom escribe (con ediciones):

El mismo resultado se podría lograr con la simple lectura de la línea de archivo de reglas de línea en un bucle, y haciendo buscar/reemplazar para cada línea de leer.

Y, de hecho, esto es lo que se cumple esencialmente 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 administrar y mantener.

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

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

Así que empiezan con el segundo punto relativo a la ruptura de las reglas en funciones separadas en 6,3 como segue en el boceto de Niels:

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

El autor en este punto:

fue la adición de este nivel de abstracción vale la pena? 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 en la función plural inicial.

6.3. Una lista de funciones: resume las reglas codificadas en la función plural para varias funciones independientes. Este es un "trampolín" para 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 coincidentes y aplicadas, 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 las reglas. Esto se convierte en un nivel de abstracción adicional, 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ódigo duplicado 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 fáciles de mantener, 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, busca una coincidencia, aplica la regla si corresponde y va a la regla siguiente. Este es un ejemplo de un cierre.

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

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 abiertas(). Afirma que se puede lograr un mejor rendimiento utilizando un iterador, que se considera más adelante en el libro.