En su pregunta original, preguntó "¿Por qué querría pasar la definición de un método a través de otro método?" Luego, en un comentario, preguntaste "¿Por qué no modificas el código fuente real del método?" De hecho, creo que es una muy buena pregunta y difícil de responder sin agitar la mano, porque los decoradores solo se vuelven realmente útiles cuando el código alcanza un cierto nivel de complejidad. Sin embargo, creo que el punto de decoradores será más clara si se tiene en cuenta las siguientes dos funciones:
def add_x_to_sequence(x, seq):
result = []
for i in seq:
result.append(i + x)
return result
def subtract_x_from_sequence(x, seq):
result = []
for i in seq:
result.append(i - x)
return result
Ahora, estas dos funciones de ejemplo, tienen algunos defectos - en la vida real, por ejemplo, lo que probablemente acaba de volver a escribir como una lista de comprensiones, pero ignoremos los defectos obvios por el momento, y pretendamos que debe escribirlos de esta manera, como for
bucles que iteran sobre las secuencias. Ahora nos enfrentamos al problema de que nuestras dos funciones hacen casi lo mismo, que difieren solo en un momento clave. Eso significa que nos estamos repitiendo aquí! Y eso es un problema Ahora tenemos que mantener más líneas de código, dejando más espacio para que aparezcan los errores, y más espacio para que los errores se oculten una vez que hayan aparecido.
Una aproximación clásica a este problema podría ser la de crear una función que toma una función, y lo aplica a través de una orden, así:
def my_map(func, x, seq):
result = []
for i in seq:
result.append(func(i, x))
return result
Ahora todo lo que tenemos que hacer es definir funcs concretos pasar a my_map
(que en realidad es solo una versión especializada de la función incorporada map
).
def sub(a, b):
return a - b
def add(a, b):
return a + b
Y podemos usarlos como esto:
added = my_map(sub, x, seq)
Pero este enfoque tiene sus problemas. Por ejemplo, es un poco más difícil de leer que nuestras funciones independientes originales; y cada vez que deseemos sumar o restar x
de una lista de elementos, debemos especificar la función y el valor como argumentos. Si estamos haciendo esto mucho, preferimos tener un solo nombre de función que siempre se refiera a la misma acción, que mejoraría la legibilidad y facilitaría la comprensión de lo que está sucediendo en nuestro código. Nos podríamos envolver el anterior en otra función ...
def add_x_to_sequence(x, seq):
return my_map(add, x, seq)
Pero ahora estamos repitiendo pues otra vez!Y también estamos creando una proliferación de funciones, abarrotando nuestro espacio de nombres.
Los decoradores proporcionan una salida a estos problemas. En lugar de pasar una función a otra función cada vez, podemos pasarla una vez. Primero definimos una función de contenedor:
def vectorize(func):
def wrapper(x, seq):
result = []
for i in seq:
result.append(func(i, x))
return result
return wrapper
Ahora todo lo que tenemos que hacer es definir una función y pasarlo a lo anterior, envolviéndolo:
def add_x_to_sequence(a, b):
return a + b
add_x_to_sequence = vectorize(add_x_to_sequence)
O, usando la sintaxis de decorador:
@vectorize
def add_x_to_sequence(a, b):
return a + b
Ahora podemos escribir muchas diferentes funciones vectorize
d, y nuestra lógica for
para todos ellos que ocurre en un solo pl as. Ahora no tenemos que arreglar u optimizar muchas funciones diferentes por separado; todos nuestros errores relacionados con bucles y optimizaciones relacionadas con bucles ocurren en el mismo lugar; y aún obtenemos todos los beneficios de legibilidad de las funciones especialmente definidas.
¡Muchas gracias por toda su ayuda! Agradezco mucho lo claro que fue. –