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

tho*_*ast 2 python

我想在我的类中定义许多方法TestClass。我想呼唤他们的名字TestClass().method_1......TestClass().method_n

我不想间接调用它们,例如通过中间方法来TestClass().use_method('method_1', params)保持与代码其他部分的一致性。

我想动态定义我的众多方法,但我不明白为什么这个最小的示例不起作用:

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)
Run Code Online (Sandbox Code Playgroud)

输出是:

function_2
function_2
<function TestClass.__init__.<locals>._f at 0x0000022ED8F46430>
<function TestClass.__init__.<locals>._f at 0x0000022EDADCE4C0>
Run Code Online (Sandbox Code Playgroud)

当我期待的时候

function_1
function_2
Run Code Online (Sandbox Code Playgroud)

我尝试在很多地方放置一些 copy.deepcopy 但它没有帮助。

尝试用一个更简单的例子来缩小范围,我再次对结果感到惊讶:

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()
Run Code Online (Sandbox Code Playgroud)

输出是2我期待的1

关于正在发生的事情有任何提示吗?

----- 编辑以给出解决方案,感谢蒂姆·罗伯茨接受的答案(并感谢卡注意到类型(自我)而不是自我 -----

最小的例子:

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

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

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

if __name__ == '__main__':
    T = TestClass()
    T.the_method()
Run Code Online (Sandbox Code Playgroud)

输出是:

1
2
Run Code Online (Sandbox Code Playgroud)

关于动态定义方法的原始问题:

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(
                type(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)
Run Code Online (Sandbox Code Playgroud)

输出是:

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

Run Code Online (Sandbox Code Playgroud)

Tim*_*rts 5

这是 Python 的经典错误之一。您获得变量的值,变量最终得到最终值。

您可以通过“捕获”变量的值作为默认值来执行您想要的操作:

        for method_name in method_names:
            def _f(method=method_name):
                print(method)
Run Code Online (Sandbox Code Playgroud)