Search code examples
pythonclosuresargskeyword-argumentlate-binding

How to define Python functions inside a loop and use *args and **kwargs


I'm trying to print a message when instance methods get called, like below. I'm running into this problem, but I'm having trouble solving it in my case because all the solutions seem to require passing in specific arguments which I can't do here.

class MathLab:
    def add(self, a, b):
        print(a + b)

    def mult(self, a, b):
        print(a * b)


m = MathLab()

for method in [m.add, m.mult]:
    def tracked_method(*args, **kwargs):
        print("Running: " + method.__name__)
        method(*args, **kwargs)


    m.__setattr__(method.__name__, tracked_method)

m.add(5, 5)

Output

Running: mult
25

Solution

  • Would this help? Add a keyword argument with a default value in order to do early binding of method (then use that keyword argument _method in place of method inside the function).

    The whole code is shown for convenience, but the only part changed from the code in the question is the tracked_method function itself.

    class MathLab:
        def add(self, a, b):
            print(a + b)
    
        def mult(self, a, b):
            print(a * b)
    
    
    m = MathLab()
    
    for method in [m.add, m.mult]:
        def tracked_method(*args, _method=method, **kwargs):
            print("Running: " + _method.__name__)
            _method(*args, **kwargs)
    
    
        m.__setattr__(method.__name__, tracked_method)
    
    m.add(5, 5)
    

    Gives:

    Running: add
    10
    

    By the way, instead of using m.__setattr__(...), you could simply use setattr(m, ...).