如何从类中的方法动态创建模块级函数

发布于 2024-09-17 23:17:31 字数 5175 浏览 8 评论 0原文

我正在尝试从类中的方法动态创建模块级函数。因此,对于类中的每个方法,我想创建一个具有相同名称的函数,该函数创建该类的实例,然后调用该方法。

我想这样做的原因是我可以采用面向对象的方法来创建 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)

似乎正在重用对函数的引用?有什么想法吗?

更新:这是 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/

I am trying to dynamically create module level functions from the methods in a class. So for every method in a class, I want to create a function with the same name which creates an instance of the class and then calls the method.

The reason I want to do this is so I can take an object-oriented approach to creating Fabric files. Since Fabric will call module level functions but not methods of a class, this is my work-around.

I have used the following links to get me started

And I have come up with the following code

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()

And I get the following error

['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)

It seems to be reusing the reference to the function? Any ideas?

UPDATE: Here is the working code with Ned Batchelder's fix

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)

UPDATE 2: Here is my blog post on the subject: http://www.saltycrane.com/blog/2010/09/class-based-fabric-scripts-metaprogramming-hack/

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

天生の放荡 2024-09-24 23:17:31

你的解决方案考虑得太多了。将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 返回的绑定方法是一个可调用的东西。只需将其分配给您的模块属性,就可以了。

You're over-thinking your solution. Change the end of fabric_class_to_function_magic to be this:

    tc = TestClass()
    func = getattr(tc, method_name)

    # add the new function to the current module
    setattr(module_obj, method_name, func)

and it works fine. No need to make a new function object, you already have one returned by getattr on your object. The bound method returned by getattr is a callable thing. Just assign it to your module attribute, and you are good to go.

温折酒 2024-09-24 23:17:31

实际上你的代码是正确的,但是当 return 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'

actually your code is right but when return func(*args, **kwargs) executes, args will pass empty tuple like () and there are no parameters in your method2, so it raises such exception,

a quick solution towards your problem would be, like

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

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

    def method2(self, *args, **kw):
        print 'method 2'
惟欲睡 2024-09-24 23:17:31

这可能对某人有用:

import inspect
import sys

def get_functions_from_class_in_module_and_extract_to_module(module, *args, class_config={}, **kwargs):

    classes = dict(tuple(x) for x in inspect.getmembers(sys.modules[module.__name__], inspect.isclass))

    for cls_name, cls in classes.items():

        class_instance = cls(**class_config[cls_name])

        functions = dict(tuple(x) for x in inspect.getmembers(cls, inspect.isfunction))

        for function_name, func in functions.items():
                
            class_func = lambda *args, **kwargs: func(class_instance, *args, **kwargs)

            setattr(module, function_name, class_func)

This might be useful to someone:

import inspect
import sys

def get_functions_from_class_in_module_and_extract_to_module(module, *args, class_config={}, **kwargs):

    classes = dict(tuple(x) for x in inspect.getmembers(sys.modules[module.__name__], inspect.isclass))

    for cls_name, cls in classes.items():

        class_instance = cls(**class_config[cls_name])

        functions = dict(tuple(x) for x in inspect.getmembers(cls, inspect.isfunction))

        for function_name, func in functions.items():
                
            class_func = lambda *args, **kwargs: func(class_instance, *args, **kwargs)

            setattr(module, function_name, class_func)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文