I want to define many methods in my class TestClass.
I want to call them by their name 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.
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.
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.
Any hint about what is happening?
----- Edited to give solution thanks to the accepted answer from Tim Roberts (and thanks cards for noticing the type(self) instead of self-----
Minimal example:
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()
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(
                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)
Output is:
method_1
method_2
method_2
method_2
<function TestClass.__init__.<locals>._f at 0x000001D1CF9D6430>
<function TestClass.__init__.<locals>._f at 0x000001D1D187E4C0>