ホームページ バックエンド開発 Python チュートリアル PythonプログラミングでMonkey Patchを開発する方法

PythonプログラミングでMonkey Patchを開発する方法

Mar 02, 2017 pm 04:04 PM

Monkey Patch モンキーパッチ手法とは、プログラムの元のコードを変更せずに、クラスまたはモジュールを追加することによって、プログラムの実行プロセス中にコードを追加することを指します。以下では、Monkey Patch モンキーパッチ開発手法の適用についてさらに詳しく説明します。 Python プログラミングでは、

モンキー パッチは、ホット パッチの目的を達成するために実行時に既存のコードを変更することです。この手法は、ソケットなどの標準ライブラリ内のコンポーネントを置き換えるために、Eventlet で広く使用されています。まず、モンキーパッチの最も単純な実装を見てみましょう。

class Foo(object):
  def bar(self):
    print 'Foo.bar'

def bar(self):
  print 'Modified bar'

Foo().bar()

Foo.bar = bar

Foo().bar()
ログイン後にコピー

Pythonの名前空間はオープンであり、dictを通じて実装されているため、パッチ適用の目的を簡単に達成できます。 Python 名前空間

python には、

ローカル
  • グローバル
  • builtin
  • といういくつかの名前空間があります。関数はグローバルに属します。

Pythonモジュールのインポートと名前の検索

モジュールをインポートするとき、Pythonは次のことを行います

モジュールをインポートします
  • モジュールオブジェクトをsys.modulesに追加し、モジュールを使用しますimport は dict から直接取得されます
  • モジュールオブジェクトをグローバル dict に追加します
  • モジュールを参照するときは、グローバルから検索されます。ここで標準モジュールを置き換える場合は、次の 2 つのことを行う必要があります
独自のモジュールを sys.modules に追加し、元のモジュールを置き換えます。置き換えられたモジュールがロードされていない場合は、最初にそれをロードする必要があります。そうしないと、最初に標準モジュールがロードされます。 (ここで利用可能なインポートフックがありますが、これにはフックを自分で実装する必要があります。このメソッドフックモジュールインポートを使用することもできます)

置き換えられたモジュールが他のモジュールを参照している場合は、それも置き換える必要がありますが、変更することはできますここでは globals dict を使用し、モジュールをグローバルに追加して、これらの参照モジュールをフックします。

イベントレット パッチャーの実装

次に、イベントレット内のパッチャー呼び出しコードを見てみましょう。このコードは、標準の ftplib にモンキー パッチを作成し、イベントレットの GreenSocket を標準のソケットに置き換えます。

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
ログイン後にコピー

コメントはコードの意図をより明確に説明しています。コードは比較的理解しやすいです。モジュールをロードするためのモジュール名 (文字列) を提供する関数 __import__ があります。インポートまたはリロード時に指定する名前はオブジェクトです。

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)
ログイン後にコピー

このコードの機能は、標準 ftplib のオブジェクトをイベントレットの ftplib モジュールに追加することです。 eventlet.ftplib で inject を呼び出してグローバルに渡し、inject ではモジュールを手動で __import__ し、モジュール オブジェクトを 1 つだけ取得したため、モジュール内のオブジェクトはグローバルに追加されず、手動で追加する必要があります。

ここで from ftplib import * が使用されていないのは、おそらく ftplib を完全に置き換えることができないためです。 from...import * は __init__.py の __all__ リストに従ってパブリック シンボルをインポートするため、この方法ではアンダースコアで始まるプライベート シンボルはインポートされず、完全なパッチ適用は達成できません。

Python プログラミングでの Monkey Patch 開発方法に関連するその他の記事については、PHP 中国語 Web サイトに注目してください。


このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

LinuxターミナルでPythonバージョンを表示するときに発生する権限の問題を解決する方法は? LinuxターミナルでPythonバージョンを表示するときに発生する権限の問題を解決する方法は? Apr 01, 2025 pm 05:09 PM

LinuxターミナルでPythonバージョンを表示する際の許可の問題の解決策PythonターミナルでPythonバージョンを表示しようとするとき、Pythonを入力してください...

プロジェクトの基本と問題駆動型の方法で10時間以内にコンピューター初心者プログラミングの基本を教える方法は? プロジェクトの基本と問題駆動型の方法で10時間以内にコンピューター初心者プログラミングの基本を教える方法は? Apr 02, 2025 am 07:18 AM

10時間以内にコンピューター初心者プログラミングの基本を教える方法は?コンピューター初心者にプログラミングの知識を教えるのに10時間しかない場合、何を教えることを選びますか...

あるデータフレームの列全体を、Python内の異なる構造を持つ別のデータフレームに効率的にコピーする方法は? あるデータフレームの列全体を、Python内の異なる構造を持つ別のデータフレームに効率的にコピーする方法は? Apr 01, 2025 pm 11:15 PM

PythonのPandasライブラリを使用する場合、異なる構造を持つ2つのデータフレーム間で列全体をコピーする方法は一般的な問題です。 2つのデータがあるとします...

中間の読書にどこでもfiddlerを使用するときにブラウザによって検出されないようにするにはどうすればよいですか? 中間の読書にどこでもfiddlerを使用するときにブラウザによって検出されないようにするにはどうすればよいですか? Apr 02, 2025 am 07:15 AM

fiddlereveryversings for the-middleの測定値を使用するときに検出されないようにする方法

uvicornは、serving_forever()なしでhttpリクエストをどのように継続的に聞いていますか? uvicornは、serving_forever()なしでhttpリクエストをどのように継続的に聞いていますか? Apr 01, 2025 pm 10:51 PM

UvicornはどのようにしてHTTPリクエストを継続的に聞きますか? Uvicornは、ASGIに基づく軽量のWebサーバーです。そのコア機能の1つは、HTTPリクエストを聞いて続行することです...

文字列を介してオブジェクトを動的に作成し、Pythonでメソッドを呼び出す方法は? 文字列を介してオブジェクトを動的に作成し、Pythonでメソッドを呼び出す方法は? Apr 01, 2025 pm 11:18 PM

Pythonでは、文字列を介してオブジェクトを動的に作成し、そのメソッドを呼び出す方法は?これは一般的なプログラミング要件です。特に構成または実行する必要がある場合は...

See all articles