クラス内のメソッドからモジュールレベル機能を動的に作成する方法
-
01-10-2019 - |
質問
クラス内のメソッドからモジュールレベルの関数を動的に作成しようとしています。したがって、クラス内のすべてのメソッドについて、クラスのインスタンスを作成してメソッドを呼び出す同じ名前の関数を作成したいと思います。
これをやりたい理由は、ファブリックファイルを作成するためのオブジェクト指向のアプローチを取ることができるためです。ファブリックはクラスの方法ではなくモジュールレベルの関数を呼び出すため、これは私の回避策です。
私は次のリンクを使用して始めました
- Pythonクラスでメソッドのリストを取得するにはどうすればよいですか?
- Pythonモジュールに機能を動的に追加します
- 現在のモジュールでsetattr()を呼び出すにはどうすればよいですか?
- http://effbot.org/zone/python-getattr.htm
- Pythonに関数の名前を持つ文字列からモジュールの関数を呼び出す
- Pythonのローカルネームスペースを変更する方法
そして、私は次のコードを思いつきました
import inspect
import sys
import types
class TestClass(object):
def __init__(self):
pass
def method1(self, arg1):
print 'method 1 %s' % arg1
def method2(self):
print 'method 2'
def fabric_class_to_function_magic(module_name):
# get the module as an object
print module_name
module_obj = sys.modules[module_name]
print dir(module_obj)
# Iterate over the methods of the class and dynamically create a function
# for each method that calls the method and add it to the current module
for method in inspect.getmembers(TestClass, predicate=inspect.ismethod):
print
print method
method_name, method_obj = method
# create a new template function which calls the method
def newfunc_template(*args, **kwargs):
tc = TestClass()
func = getattr(tc, method_name)
return func(*args, **kwargs)
# create the actual function
print 'code: ', newfunc_template.func_code
print 'method_name: ', method_name
newfunc = types.FunctionType(newfunc_template.func_code,
{'TestClass': TestClass,
'getattr': getattr,
'method_name': method_name,
},
name=method_name,
argdefs=newfunc_template.func_defaults,
closure=newfunc_template.func_closure,
)
# add the new function to the current module
setattr(module_obj, method_name, newfunc)
# test the dynamically created module level function
thismodule = sys.modules[__name__]
print dir(thismodule)
fabric_class_to_function_magic(__name__)
print dir(thismodule)
method1('arg1')
method2()
そして、次のエラーが発生します
['TestClass', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'fabric_class_to_function_magic', 'inspect', 'sys', 'thismodule', 'types']
__main__
['TestClass', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'fabric_class_to_function_magic', 'inspect', 'sys', 'thismodule', 'types']
('__init__', <unbound method TestClass.__init__>)
code: <code object newfunc_template at 0x7f8800a28d50, file "test.py", line 85>
method_name: __init__
('method1', <unbound method TestClass.method1>)
code: <code object newfunc_template at 0x7f8800a28d50, file "test.py", line 85>
method_name: method1
('method2', <unbound method TestClass.method2>)
code: <code object newfunc_template at 0x7f8800a28d50, file "test.py", line 85>
method_name: method2
['TestClass', '__builtins__', '__doc__', '__file__', '__init__', '__name__', '__package__', 'fabric_class_to_function_magic', 'inspect', 'method1', 'method2', 'sys', 'thismodule', 'types']
Traceback (most recent call last):
File "test.py", line 111, in <module>
method1('arg1')
File "test.py", line 88, in newfunc_template
return func(*args, **kwargs)
TypeError: method2() takes exactly 1 argument (2 given)
関数への参照を再利用しているようですか?何か案は?
更新:NED Batchelderの修正を備えた作業コードは次のとおりです
def fabric_class_to_function_magic(module_name):
# get the module as an object
module_obj = sys.modules[module_name]
# Iterate over the methods of the class and dynamically create a function
# for each method that calls the method and add it to the current module
for method in inspect.getmembers(TestClass, predicate=inspect.ismethod):
method_name, method_obj = method
# get the bound method
tc = TestClass()
func = getattr(tc, method_name)
# add the function to the current module
setattr(module_obj, method_name, func)
更新2:これがこのテーマに関する私のブログ投稿です: http://www.saltycrane.com/blog/2010/09/class Based-fabric-scripts-metaprogramming-hack/
解決
あなたはあなたの解決策を考えすぎています。の終わりを変更します fabric_class_to_function_magic
これになる:
tc = TestClass()
func = getattr(tc, method_name)
# add the new function to the current module
setattr(module_obj, method_name, func)
そして、それは正常に機能します。新しい関数オブジェクトを作成する必要はありません。オブジェクトのGetattrによってすでに返されているものがあります。 getattrによって返されるバウンドメソッドは、呼ばれるものです。モジュール属性に割り当てるだけで、行ってもいいです。
他のヒント
実際にあなたのコードは正しいですが、return func(*args、** kwargs)が実行されると、argは空のタプルのように渡され、メソッド2にパラメーターはありません。
あなたの問題に対する迅速な解決策は、
class TestClass(object):
def __init__(self):
pass
def method1(self, arg1):
print 'method 1 %s' % arg1
def method2(self, *args, **kw):
print 'method 2'