Cómo entender el cierre de la función de Python
1. El concepto de cierre
En primer lugar, tenemos que empezar con el concepto básico ¿Qué es el cierre? Echemos un vistazo a la explicación en la wiki:
Copie el código de la siguiente manera:
En informática, cierre (Closure) es la abreviatura de cierre léxico (Lexical Closure) y es una función de referencia de variables libres. La variable libre a la que se hace referencia permanecerá con la función incluso después de que haya abandonado el entorno en el que fue creada. Por tanto, hay otra forma de decir que un cierre es una entidad compuesta por una función y su entorno de referencia asociado. Los cierres pueden tener múltiples instancias en tiempo de ejecución, y diferentes entornos de referencia y la misma combinación de funciones pueden producir diferentes instancias.
....
Arriba se mencionan dos puntos clave: variables y funciones libres. Estas dos claves se analizarán más adelante. Todavía necesito explicar el significado de "cierre". Como deja claro el texto, se puede entender claramente como un paquete cerrado. Este paquete es una función. Por supuesto, también existe la lógica correspondiente. dentro del paquete hay variables libres, las variables libres pueden deambular con el paquete. Por supuesto, debe existir la premisa de que se crea este paquete.
Presentando a través del lenguaje Python, un cierre significa que usted llama a una función A, y esta función A le devuelve una función B. Esta función B devuelta se llama cierre. Los parámetros que pasa al llamar a la función A son variables libres.
Por ejemplo:
Copia el código de la siguiente manera:
def func(nombre):
def internal_func(age):
imprimir 'nombre:', nombre, 'edad:', edad
return internal_func
bb = func('the5fire')
bb(26) # >>> nombre: the5fire edad: 26
Cuando se llama a func aquí, se genera un cierre - internal_func, y el cierre contiene la variable libre - nombre, entonces esto también significa que cuando finaliza el ciclo de vida de la función func, el nombre de la variable todavía existe porque el cierre hace referencia a ella, por lo que no se reciclará.
Además, el cierre no es un concepto único en Python. Todos los lenguajes que tratan las funciones como ciudadanos de primera clase tienen el concepto de cierre. Sin embargo, los cierres también se pueden usar en lenguajes como Java donde las clases son ciudadanos de primera clase, pero deben implementarse mediante clases o interfaces.
Para obtener más información conceptual, consulte el enlace de referencia al final.
2. ¿Por qué usar cierres?
Basado en la introducción anterior, me pregunto si los lectores sienten que esto es algo similar a las clases. La similitud es que ambas proporcionan encapsulación de datos. La diferencia es que el cierre en sí es un método. Al igual que las clases, a menudo abstraemos cosas comunes en clases cuando programamos (por supuesto, además de modelar el mundo real de los negocios) para reutilizar funciones comunes. Lo mismo ocurre con los cierres cuando necesitamos abstracción granular de funciones, los cierres son una buena opción.
En este punto, un cierre puede entenderse como un objeto de solo lectura. Puede pasarle una propiedad, pero solo puede proporcionarle una interfaz de ejecución. Por lo tanto, en los programas a menudo necesitamos un objeto de función de este tipo, el cierre, para ayudarnos a completar una función común, como el decorador que se mencionará más adelante.
3. Usar cierres
El primer escenario, un escenario de uso muy importante y común en Python, es el decorador. Python proporciona un método muy amigable para el decorador: "Azúcar sintáctico". @, nos permite usar decoradores de manera muy conveniente. El principio de decoración no se explicará demasiado. En resumen, si agrega @decorator_func a una función func, es equivalente a decorator_func(func):
. Copie el código de la siguiente manera:
def decorator_func(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
envoltorio de devolución
@decorator_func
def func(nombre):
imprimir 'mi nombre es', nombre
# es equivalente a
decorator_func(func)
En este ejemplo del decorador, el cierre (envoltorio) contiene El parámetro func externo puede aceptar los parámetros pasados desde el exterior Los parámetros aceptados se pasan intactos a func y se devuelve el resultado de la ejecución.
Este es un ejemplo simple. Si es un poco más complicado, puede tener múltiples cierres, como el decorador LRUCache de uso frecuente. El decorador puede aceptar parámetros como @lru_cache(expire=500). La implementación es el anidamiento de dos cierres:
Copia el código de la siguiente manera:
def lru_cache(expire=5):
# Tiempo de espera predeterminado de 5 s
def func_wrapper(func):
def internal(*args, **kwargs):
# procesamiento de caché bala bala bala
return func(*args, **kwargs)
return internal
return func_wrapper
@lru_cache(expire=10*60)
def get(request, pk)
# Omitir código específico
devolver respuesta()
Los estudiantes que no comprenden los cierres deben poder comprender lo anterior código, esta es una pregunta de entrevista que hemos hecho a menudo en entrevistas anteriores.
El segundo escenario se basa en una característica de cierre: la "evaluación diferida". Esta aplicación es más común al acceder a la base de datos, por ejemplo:
Copia el código de la siguiente manera:
# Ilustración de pseudocódigo
clase QuerySet(objeto) :
def __init__(self, sql):
self.sql = sql
self.db = Mysql.connect().corsor() # Pseudo Código
def __call__(self):
return db.execute(self.sql)
def query(sql):
return QuerySet(sql)
resultado = query("seleccione nombre de user_app")
si es hora > ahora:
imprimir resultado # Ejecutar la base de datos en este time Access
El ejemplo inapropiado anterior muestra la función de evaluación diferida mediante cierre, pero el resultado devuelto por la consulta anterior no es una función, sino una clase con funciones funcionales.
Si está interesado, puede echar un vistazo a la implementación del conjunto de consultas de Django. El principio es similar.
El tercer escenario es la situación en la que los parámetros de una determinada función deben asignarse con anticipación. Por supuesto, existe una buena solución en Python para acceder a functools.parial, pero también se puede lograr usando. cierres.
Copia el código de la siguiente manera:
def parcial(**outer_kwargs):
def wrapper(func):
def interior ( *args, **kwargs):
para k, v en external_kwargs.items():
kwargs[k] = v
return func( * args, **kwargs)
return internal
retorno de envoltura
@partial(age=15)
def say(nombre = Ninguno, edad=Ninguno):
imprimir nombre, edad
say(nombre="the5fire")
# Por supuesto, usar functools es mucho más sencillo que esto
# Solo necesito: functools.partial(say, age=15)(name='the5fire')
Parece que este es otro ejemplo inverosímil, pero Puede considerarse como un ejemplo práctico de aplicación de paquete.