38

ソケットモジュールを使用できるようにするには、ソケットモジュールのコピーを作成し、もう1つのソケットモジュールにモンキーパッチを適用して、別の方法で使用する必要があります。

これは可能ですか?

つまり、モジュールを実際にコピーすることを意味します。つまり、実行時に、コピーし、関数をsocketmodule.cに変更して、拡張機能としてインストールした場合と同じ結果を取得します。initsocket()initmy_socket()my_socket

4

4 に答える 4

36

モジュールをインポートしてからsys.modulesから削除したり、モジュールをコピーしようとしたりするなど、いつでもトリックを実行できます。ただし、Pythonはすでに標準ライブラリで必要なものを提供しています。

import imp # Standard module to do such things you want to.

# We can import any module including standard ones:
os1=imp.load_module('os1', *imp.find_module('os'))

# Here is another one:
os2=imp.load_module('os2', *imp.find_module('os'))

# This returns True:
id(os1)!=id(os2)

Python3.3 +

imp.load_modulepython3.3 +では非推奨であり、importlib

#!/usr/bin/env python3

import sys
import importlib.util

SPEC_OS = importlib.util.find_spec('os')
os1 = importlib.util.module_from_spec(SPEC_OS)
SPEC_OS.loader.exec_module(os1)
sys.modules['os1'] = os1

os2 = importlib.util.module_from_spec(SPEC_OS)
SPEC_OS.loader.exec_module(os2)
sys.modules['os2'] = os2
del SPEC_OS

assert os1 is not os2, \
    "Module `os` instancing failed"

ここでは、同じモジュールを2回インポートしますが、完全に異なるモジュールオブジェクトとしてインポートします。sys.modulesを確認すると、load_module呼び出しの最初のパラメーターとして入力した2つの名前が表示されます。詳細については、ドキュメントをご覧ください。

アップデート:

このアプローチの主な違いを明確にするために、これを明確にしたいと思います。同じモジュールをこの方法でインポートすると、実行時にインポートする他のすべてのモジュールから両方のバージョンにグローバルにアクセスできるようになります。これは、質問者が必要とするものです。わかりました。

以下は、この点を強調する別の例です。

これらの2つのステートメントは、まったく同じことを行います。

import my_socket_module as socket_imported

socket_imported = imp.load_module('my_socket_module',
    *imp.find_module('my_socket_module')
)

2行目では、「my_socket_module」文字列を2回繰り返します。これが、importステートメントの動作方法です。しかし、これらの2つの文字列は、実際には2つの異なる理由で使用されます。

find_moduleに渡した2番目のオカレンスは、システムで検出されるファイル名として使用されます。load_moduleメソッドに渡した文字列の最初の出現は、ロードされたモジュールのシステム全体の識別子として使用されます。

したがって、これらには異なる名前を使用できます。つまり、モジュールのpythonソースファイルをコピーしてロードしたのとまったく同じように機能させることができます。

socket = imp.load_module('socket_original', *imp.find_module('my_socket_module'))
socket_monkey = imp.load_module('socket_patched',*imp.find_module('my_socket_module'))

def alternative_implementation(blah, blah):
    return 'Happiness'

socket_monkey.original_function = alternative_implementation

import my_sub_module

次に、my_sub_moduleで、システムに存在しない「socket_patched」をインポートできます。ここにmy_sub_module.pyがあります。

import socket_patched
socket_patched.original_function('foo', 'bar')
# This call brings us 'Happiness'
于 2012-07-01T20:45:47.520 に答える
11

これはかなり嫌ですが、これで十分かもしれません:

import sys

# if socket was already imported, get rid of it and save a copy
save = sys.modules.pop('socket', None)

# import socket again (it's not in sys.modules, so it will be reimported)
import socket as mysock

if save is None:
    # if we didn't have a saved copy, remove my version of 'socket'
    del sys.modules['socket']
else:
    # if we did have a saved copy overwrite my socket with the original
    sys.modules['socket'] = save
于 2012-06-26T02:19:23.877 に答える
7

古いものの関数と変数を使用して新しいモジュールを作成するコードを次に示します。

def copymodule(old):
    new = type(old)(old.__name__, old.__doc__)
    new.__dict__.update(old.__dict__)
    return new

これはモジュールのかなり浅いコピーを行うことに注意してください。辞書は新しく作成されたため、基本的なモンキーパッチは機能しますが、元のモジュールの変更可能なものは2つで共有されます。

編集:コメントによると、深いコピーが必要です。copyモジュールのディープコピーをサポートするためにモジュールにモンキーパッチを適用しようとしましたが、うまくいきませんでした。次に、モジュールを2回インポートしようとしましたが、モジュールがにキャッシュされsys.modulesているため、同じモジュールが2回取得されました。最後に、私が思いついた解決策はsys.modules、最初にモジュールをインポートした後にモジュールを削除してから、再度インポートすることでした。

from imp import find_module, load_module
from sys import modules

def loadtwice(name, path=None):
    """Import two copies of a module.

    The name and path arguments are as for `find_module` in the `imp` module.
    Note that future imports of the module will return the same object as
    the second of the two returned by this function.
    """
    startingmods = modules.copy()
    foundmod = find_module(name, path)
    mod1 = load_module(name, *foundmod)
    newmods = set(modules) - set(startingmods)
    for m in newmods:
        del modules[m]
    mod2 = load_module(name, *foundmod)
    return mod1, mod2
于 2012-06-23T21:18:28.100 に答える
1

ソケットモジュールをsocket_monkeyに物理的にコピーして、そこから移動しますか?「賢い」回避策は必要ないと思いますが、単純化しすぎている可能性があります。

于 2012-06-23T17:34:36.457 に答える