Parches de mono en python: ¿Cuándo lo necesitamos?

En Python, el término monkey patch solo se refiere a modificaciones dinámicas de una clase o módulo en tiempo de ejecución. Como principiante, es realmente difícil para mí entender este término en el contexto de python. ¿Alguien me puede explicar con un ejemplo del mundo real? ¿Cómo lo hacemos exactamente?

  1. Modificaciones dinámicas de una clase.
  2. Modificaciones dinámicas del módulo en tiempo de ejecución.

Estoy insistiendo en un ejemplo del mundo real (lo más simple posible) para entender en qué escenarios tenemos que hacer esa tarea.

El parcheo de monos es una manera de hacer un cambio global bajo el capó de manera que el código existente continuará ejecutándose, pero con un comportamiento modificado.

Un ejemplo realmente trivial de cambiar el comportamiento del comando builtin str :

b.py

 def foo(msg): s = str(msg) print s, type(s) 

a.py

 import b b.foo('foo') # monkey-patch import __builtin__ __builtin__.str = unicode b.foo('foo') # Results: #foo  #foo  

El módulo a ha modificado el comportamiento de otro código usando el comando str , parcheando para usar unicode lugar. Esto sería necesario ya que pretendemos que no tenemos acceso al b.py de b.py Podría haber sido un paquete enorme que simplemente usamos y no podemos cambiar. Pero podemos deslizar en un nuevo código para ser llamado que cambia el comportamiento.

Un ejemplo del mundo real de gevent.

 >>> import gevent >>> from gevent import socket >>> urls = ['www.google.com', 'www.example.com', 'www.python.org'] >>> jobs = [gevent.spawn(socket.gethostbyname, url) for url in urls] >>> gevent.joinall(jobs, timeout=2) >>> [job.value for job in jobs] ['74.125.79.106', '208.77.188.166', '82.94.164.162'] 

El ejemplo anterior usó gevent.socket para las operaciones de socket. Si se usara el módulo de socket estándar, tardaría 3 veces más en completarse porque las solicitudes de DNS serían secuenciales. El uso del módulo de zócalo estándar dentro de los greenlets hace que gevent sea bastante inútil, entonces, ¿qué pasa con los módulos y paquetes que se construyen sobre el zócalo?

Para eso está el parche del mono. Las funciones en gevent.monkey reemplazan cuidadosamente las funciones y clases en el módulo de socket estándar con sus contrapartes cooperativas. De esa manera, incluso los módulos que desconocen gevent pueden beneficiarse de la ejecución en un entorno multi-greenlet.

 >>> from gevent import monkey; monkey.patch_socket() >>> import urllib2 # it's usable from multiple greenlets now 

Hay algunos ejemplos de la vida real cuando se usan parches de monos:

  1. Pruebas unitarias . A menudo es conveniente crear parches / simulacros parcheando un objeto real para cambiar su comportamiento para respetar las necesidades de prueba. Por ejemplo, desea probar el comportamiento de Object.method1() . El método puede devolver una cadena o Ninguno. Así que aquí viene el parche de los monos. Reemplace method1 con un método de código auxiliar que contiene solo una línea de código de return None .
  2. Código de modificación / ampliación durante el tiempo de ejecución. Posiblemente el mejor ejemplo de tal caso de uso sea la popular biblioteca Python Gevent. Utiliza el parche de mono para hacer que el módulo de zócalo estándar funcione de la manera que gevent lo necesita. Aquí hay un código fuente .
  3. Aplicar un parche a un objeto en tiempo de ejecución . Puede ser útil realizar una modificación fácil y rápida de un objeto que, después de eso, debe cumplir con alguna interfaz utilizada por otras partes de una aplicación. Se puede hacer setattr en Python usando la función setattr . Bueno, esto a menudo significa una mala architecture de su aplicación y no es una buena decisión de diseño .