Search code examples
pythonclasslambdasetattr

Python-setattr pass function with args


I'm trying to set methods of a class programmatically by calling setattr in a loop, but the reference I pass to the function that is called by this method defaults back to its last value, instead of what was passed at the time of the setattrcall. Curiously, I'm also setting the __doc__ attribute and this assignment actually works as expected:

class Foo2:

    def do_this(self, pass_this: str):
        print(pass_this)


class Foo:

    def __init__(self):
        self.reference = "ti-hihi"
        self.foo2 = Foo2()
        for (method_name, pass_this) in [("bar", "passed-for-bar"), ("bar2", "passed-for-bar2")]:
            my_doc = f"""my_custom_docstring: {pass_this}"""

            def some_func():
                self.foo2.do_this(pass_this=pass_this)
            some_func.__doc__ = my_doc
            setattr(self, method_name, some_func)


if __name__ == '__main__':
    f = Foo()
    f.bar()  # prints "pass-for-bar2" instead of "pass-for-bar"
    f.bar.__doc__  # prints "pass-for-bar" as expected

I already tried a few things but couldn't figure it out.

Things I tried:

lambda -- my best bet, tbh

def some_func(reference):
    self.foo2.do_this(pass_this=reference)
some_func.__doc__ = my_doc
setattr(self, method_name, lambda: some_func(pass_this))

deepcopy

import copy

def some_func():
    self.foo2.do_this(pass_this=copy.deepcopy(pass_this))
some_func.__doc__ = my_doc
setattr(self, method_name, some_func)

another deepcopy variant which feels dangerous if I think about the place I want to put this:

import copy

def some_func():
    self.foo2.do_this(pass_this=pass_this)
some_func.__doc__ = my_doc
setattr(self, method_name, copy.deepcopy(some_func))

... and a few combinations of those but I'm missing some crucial piece.


Solution

  • Thanks to @AndrewAllaire for this tip, using functools.partial solved it for me

    some_func = partial(self.foo2.do_this, pass_this=pass_this)