2010-09-08 35 views
8

我想從一個類中的方法動態創建模塊級功能。因此,對於類中的每個方法,我想創建一個具有相同名稱的函數來創建該類的一個實例,然後調用該方法。如何從一個類中的方法動態創建模塊級功能

我想這樣做的原因是我可以採用面向對象的方法來創建Fabric文件。由於Fabric將調用模塊級功能,但不能調用類的方法,所以這是我的解決方法。

我已經使用了以下鏈接讓我開始

,我想出了下面的代碼

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) 

這似乎是重用參考功能?有任何想法嗎?

更新:這裏是斯內德爾德的修復工作代碼

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/

+0

奇怪的是,你還應該設置'func .__ module__ = module_name'以便它能正確地描述它自己,否則它會報告它的模塊是包含'fabric_class_to_function_magic'而不是目標模塊的模塊! – F1Rumors 2017-12-01 20:10:00

回答

9

你在想你的解決方案。將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返回的綁定方法是可調用的。只要將它分配給你的模塊屬性,你就可以走了。

1

其實你的代碼是正確的,但在返回FUNC(* args,** kwargs)執行時,args將會像()一樣傳遞空元組,並且您的method2中沒有參數,因此會引發此類異常,如

class TestClass(object): 
    def __init__(self): 
     pass 

    def method1(self, arg1): 
     print 'method 1 %s' % arg1 

    def method2(self, *args, **kw): 
     print 'method 2' 
+0

這使錯誤消失,但底層問題仍然存在。現在它打印「方法2」兩次。 – saltycrane 2010-09-08 05:11:46