¿Qué es el parche de mono?
Estoy tratando de entender, ¿qué es el parche de mono o un parche de mono?
¿Es eso algo así como sobrecargar o delegar métodos/operadores?
¿Tiene algo en común con estas cosas?
No, no es como ninguna de esas cosas. Es simplemente el reemplazo dinámico de atributos en tiempo de ejecución.
Por ejemplo, considere una clase que tiene un método get_data
. Este método realiza una búsqueda externa (en una base de datos o API web, por ejemplo) y varios otros métodos de la clase lo llaman. Sin embargo, en una prueba unitaria, no desea depender de la fuente de datos externa, por lo que reemplaza dinámicamente el get_data
método con un código auxiliar que devuelve algunos datos fijos.
Debido a que las clases de Python son mutables y los métodos son solo atributos de la clase, puedes hacer esto tanto como quieras y, de hecho, incluso puedes reemplazar clases y funciones en un módulo exactamente de la misma manera.
Pero, como señaló un comentarista , tenga cuidado al aplicar parches a los monos:
Si algo más además de su lógica de prueba
get_data
también llama, también llamará a su reemplazo parcheado en lugar del original, lo cual puede ser bueno o malo. Sólo ten cuidado.Si existe alguna variable o atributo que también apunta a la
get_data
función cuando la reemplazas, este alias no cambiará su significado y seguirá apuntando al originalget_data
. (¿Por qué? Python simplemente vuelve a vincular el nombreget_data
de su clase a algún otro objeto de función; otras vinculaciones de nombres no se ven afectadas en absoluto).
Un MonkeyPatch es una pieza de código Python que extiende o modifica otro código en tiempo de ejecución (normalmente al inicio).
Un ejemplo simple se ve así:
from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
return "ook ook eee eee eee!"
SomeClass.speak = speak
Fuente: página de MonkeyPatch en la wiki de Zope.
¿Qué es un parche de mono?
En pocas palabras, el parcheo de mono consiste en realizar cambios en un módulo o clase mientras el programa se está ejecutando.
Ejemplo de uso
Hay un ejemplo de parcheo de monos en la documentación de Pandas:
import pandas as pd
def just_foo_cols(self):
"""Get a list of column names containing the string 'foo'
"""
return [x for x in self.columns if 'foo' in x]
pd.DataFrame.just_foo_cols = just_foo_cols # monkey-patch the DataFrame class
df = pd.DataFrame([list(range(4))], columns=["A","foo","foozball","bar"])
df.just_foo_cols()
del pd.DataFrame.just_foo_cols # you can also remove the new method
Para desglosar esto, primero importamos nuestro módulo:
import pandas as pd
A continuación creamos una definición de método, que existe independiente y libre fuera del alcance de cualquier definición de clase (dado que la distinción entre una función y un método independiente no tiene mucho sentido, Python 3 elimina el método independiente):
def just_foo_cols(self):
"""Get a list of column names containing the string 'foo'
"""
return [x for x in self.columns if 'foo' in x]
A continuación simplemente adjuntamos ese método a la clase en la que queremos usarlo:
pd.DataFrame.just_foo_cols = just_foo_cols # monkey-patch the DataFrame class
Y luego podemos usar el método en una instancia de la clase y eliminar el método cuando hayamos terminado:
df = pd.DataFrame([list(range(4))], columns=["A","foo","foozball","bar"])
df.just_foo_cols()
del pd.DataFrame.just_foo_cols # you can also remove the new method
Advertencia sobre la manipulación de nombres
Si está utilizando la alteración de nombres (anteponiendo atributos con un guión bajo doble, lo que altera el nombre y lo cual no recomiendo), tendrá que modificar los nombres manualmente si hace esto. Como no recomiendo la manipulación de nombres, no lo demostraré aquí.
Ejemplo de prueba
¿Cómo podemos utilizar este conocimiento, por ejemplo, en las pruebas?
Digamos que necesitamos simular una llamada de recuperación de datos a una fuente de datos externa que resulta en un error, porque queremos garantizar el comportamiento correcto en tal caso. Podemos parchear la estructura de datos para garantizar este comportamiento. (Entonces, usando un nombre de método similar al sugerido por Daniel Roseman :)
import datasource
def get_data(self):
'''monkey patch datasource.Structure with this to simulate error'''
raise datasource.DataRetrievalError
datasource.Structure.get_data = get_data
Y cuando probamos el comportamiento que depende de que este método genere un error, si se implementa correctamente, obtendremos ese comportamiento en los resultados de la prueba.
Simplemente hacer lo anterior alterará el Structure
objeto durante la vida del proceso, por lo que querrás usar configuraciones y desmontajes en tus pruebas unitarias para evitar hacerlo, por ejemplo:
def setUp(self):
# retain a pointer to the actual real method:
self.real_get_data = datasource.Structure.get_data
# monkey patch it:
datasource.Structure.get_data = get_data
def tearDown(self):
# give the real method back to the Structure object:
datasource.Structure.get_data = self.real_get_data
(Si bien lo anterior está bien, probablemente sería una mejor idea usar la mock
biblioteca para parchear el código. mock
El patch
decorador sería menos propenso a errores que hacer lo anterior, lo que requeriría más líneas de código y, por lo tanto, más oportunidades de introducir errores. . Todavía tengo que revisar el código, mock
pero imagino que utiliza el parche de mono de manera similar).
Según Wikipedia :
En Python, el término parche mono solo se refiere a modificaciones dinámicas de una clase o módulo en tiempo de ejecución, motivadas por la intención de parchear código de terceros existente como solución alternativa a un error o característica que no actúa como usted desea.
Primero: el parcheo de monos es un truco malvado (en mi opinión).
A menudo se utiliza para reemplazar un método a nivel de módulo o clase con una implementación personalizada.
El caso de uso más común es agregar una solución alternativa para un error en un módulo o clase cuando no se puede reemplazar el código original. En este caso, reemplaza el código "incorrecto" mediante parches de mono con una implementación dentro de su propio módulo/paquete.