简体   繁体   English

如何在python类中动态定义方法?

[英]How to define methods dynamically inside a python class?

I want to define many methods in my class TestClass .我想在我的类TestClass定义许多方法。 I want to call them by their name TestClass().method_1 ... TestClass().method_n .我想用他们的名字来称呼他们TestClass().method_1 ... TestClass().method_n

I do not want to call them indirectly for example through an intermediate method like TestClass().use_method('method_1', params) to keep consistency with other parts of the code.我不想间接调用它们,例如通过像TestClass().use_method('method_1', params)这样的中间方法来保持与代码的其他部分的一致性。

I want to define dynamically my numerous methods, but I do not understand why this minimal example does not work:我想动态定义我的众多方法,但我不明白为什么这个最小的例子不起作用:

class TestClass:
    def __init__(self):
        method_names = [
            'method_1',
            'method_2']
        
        for method_name in method_names:
            def _f():
                print(method_name)
            # set the method as attribute
            # (that is OK for me that it will not be
            #   a bound method)
            setattr(
                self,
                method_name,
                _f)
            del _f

if __name__ == '__main__':
    T = TestClass()

    T.method_1()
    T.method_2()
    
    print(T.method_1)
    print(T.method_2)

Output is:输出是:

function_2
function_2
<function TestClass.__init__.<locals>._f at 0x0000022ED8F46430>
<function TestClass.__init__.<locals>._f at 0x0000022EDADCE4C0>

while I was expecting当我期待的时候

function_1
function_2

I tried to put some copy.deepcopy at many places but it does not help.我试图在很多地方放一些 copy.deepcopy 但它没有帮助。

Trying to narrow it down with a more minimal example, I am again surprised by the result:试图用一个更简单的例子缩小它的范围,我再次对结果感到惊讶:

class TestClass:
    def __init__(self):
        variable = 1

        def _f():
            print(variable)
        
        del variable

        setattr(
            self,
            'the_method',
            _f)
        del _f
        
        variable = 2

if __name__ == '__main__':
    T = TestClass()
    T.the_method()

Output is 2 while I was expecting 1 .输出是2而我期待1

Any hint about what is happening?关于发生了什么的任何提示?

Edited to give solution thanks to the accepted answer from Tim Roberts由于蒂姆·罗伯茨接受的答案,已编辑以提供解决方案

Minimal example:最小的例子:

class TestClass:
    def __init__(self):
        variable = 1

        def _f(captured_variable=variable):
            print(captured_variable)
            print(variable)
        
        del variable

        setattr(
            self,
            'the_method',
            _f)
        del _f
        
        variable = 2

if __name__ == '__main__':
    T = TestClass()
    T.the_method()

Output is:输出是:

1
2

And original question about dynamically defined methods:关于动态定义方法的原始问题:

class TestClass:
    def __init__(self):
        method_names = [
            'method_1',
            'method_2']
        
        for method_name in method_names:
            def _f(captured_method=method_name):

                print(captured_method)
                print(method_name)
                
            # set the method as attribute
            setattr(
                self,
                method_name,
                _f)
            del _f

if __name__ == '__main__':
    T = TestClass()

    T.method_1()
    T.method_2()
    
    print(T.method_1)
    print(T.method_2)

Output is:输出是:

method_1
method_2
method_2
method_2
<function TestClass.__init__.<locals>._f at 0x000001D1CF9D6430>
<function TestClass.__init__.<locals>._f at 0x000001D1D187E4C0>

This is one of the classic Python stumbles.这是典型的 Python 绊脚石之一。 Your get the value of the variable, and the variable ends up with the final value.你得到变量的值,变量以最终值结束。

You can do what you want by "capturing" the value of the variable as a default:您可以通过“捕获”变量的值作为默认值来执行您想要的操作:

        for method_name in method_names:
            def _f(method=method_name):
                print(method)

To make clearer how to fix the problem with the scope, you could pass the method as a key parameter of the lambda function directly and a possibility would be:为了更清楚地说明如何解决范围问题,您可以将该方法作为 lambda 函数的关键参数直接传递,一种可能性是:

class TestClass:
    def __init__(self, *methods):
        self.methods = methods

    def add_methods(self):
        for method in self.methods:
            setattr(type(self), method.__name__, lambda self, m=method: m())

def method_1(): return '1'
def method_2(): return '2'

t = TestClass(method_1, method_2)

t.add_methods()
print(t.method_1())
print(t.method_2())

Output输出

1
2

I modify a bit the original code but notice setattr(type(self), ...) !!我修改了一些原始代码,但请注意setattr(type(self), ...) !!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM