Die Monkey Patch-Methode bezieht sich auf das Hinzufügen von Code während des laufenden Prozesses des Programms durch Hinzufügen von Klassen oder Modulen, ohne den ursprünglichen Code des Programms zu ändern. Im Folgenden finden Sie eine weitere detaillierte Erläuterung der Monkey Patch-Entwicklungsmethode Die Anwendung des
Monkey-Patches besteht darin, den vorhandenen Code zur Laufzeit zu ändern, um den Zweck des Hot-Patches zu erreichen. Diese Technik wird in Eventlet häufig verwendet, um Komponenten in der Standardbibliothek, wie z. B. Sockets, zu ersetzen. Schauen wir uns zunächst die einfachste Implementierung von Monkey Patch an.
class Foo(object): def bar(self): print 'Foo.bar' def bar(self): print 'Modified bar' Foo().bar() Foo.bar = bar Foo().bar()
Da der Namespace in Python offen ist und über dict implementiert wird, ist es einfach, den Zweck des Patchens zu erreichen.
Python-Namespace
Python hat mehrere Namespaces, nämlich
Locals
globals
eingebaut
In Funktionen definierte Variablen gehören zu lokalen Variablen, während in Modulen definierte Funktionen zu globalen Variablen gehören.
Python-Modulimport und Namenssuche
Wenn wir ein Modul importieren, führt Python die folgenden Dinge aus:
Importieren eines Modul
und fügen Sie das Modulobjekt zu sys.modules hinzu. Nachfolgende Importe des Moduls werden direkt aus dem Diktat abgerufen Modulobjekt wird zum Globals-Dikt hinzugefügt
Wenn wir auf ein Modul verweisen, wird es anhand von Globals durchsucht. Wenn wir hier ein Standardmodul ersetzen möchten, müssen wir die folgenden zwei Dinge tun:
Werfen wir nun einen Blick auf den aufrufenden Code von Patcher im Eventlet. Dieser Code erstellt einen Monkey-Patch für die Standard-FTPLIB und ersetzt den Standard-GreenSocket des Eventlet.-Socket.
from eventlet import patcher # *NOTE: there might be some funny business with the "SOCKS" module # if it even still exists from eventlet.green import socket patcher.inject('ftplib', globals(), ('socket', socket)) del patcher inject函数会将eventlet的socket模块注入标准的ftplib中,globals dict被传入以做适当的修改。 让我们接着来看一下inject的实现。 __exclude = set(('__builtins__', '__file__', '__name__')) def inject(module_name, new_globals, *additional_modules): """Base method for "injecting" greened modules into an imported module. It imports the module specified in *module_name*, arranging things so that the already-imported modules in *additional_modules* are used when *module_name* makes its imports. *new_globals* is either None or a globals dictionary that gets populated with the contents of the *module_name* module. This is useful when creating a "green" version of some other module. *additional_modules* should be a collection of two-element tuples, of the form (, ). If it's not specified, a default selection of name/module pairs is used, which should cover all use cases but may be slower because there are inevitably redundant or unnecessary imports. """ if not additional_modules: # supply some defaults additional_modules = ( _green_os_modules() + _green_select_modules() + _green_socket_modules() + _green_thread_modules() + _green_time_modules()) ## Put the specified modules in sys.modules for the duration of the import saved = {} for name, mod in additional_modules: saved[name] = sys.modules.get(name, None) sys.modules[name] = mod ## Remove the old module from sys.modules and reimport it while ## the specified modules are in place old_module = sys.modules.pop(module_name, None) try: module = __import__(module_name, {}, {}, module_name.split('.')[:-1]) if new_globals is not None: ## Update the given globals dictionary with everything from this new module for name in dir(module): if name not in __exclude: new_globals[name] = getattr(module, name) ## Keep a reference to the new module to prevent it from dying sys.modules['__patched_module_' + module_name] = module finally: ## Put the original module back if old_module is not None: sys.modules[module_name] = old_module elif module_name in sys.modules: del sys.modules[module_name] ## Put all the saved modules back for name, mod in additional_modules: if saved[name] is not None: sys.modules[name] = saved[name] else: del sys.modules[name] return module
if new_globals is not None: ## Update the given globals dictionary with everything from this new module for name in dir(module): if name not in __exclude: new_globals[name] = getattr(module, name)