marcos
marcos

Reputation: 4510

Automatic override of parent methods

I'm trying to add a wrapper to each method in a class by subclassing it, and reassigning them in the constructor of the new class, however i'm getting the same reference for all subclassed methods, how is this possible?

class A:
    def foo(self):
        print("foo")

    def bar(self):
        print("bar")


class B(A):
    def __init__(self):
        super().__init__()

        methods = [
            (method_name, getattr(self, method_name)) for method_name in dir(self) if not method_name.startswith('_')
        ]

        for (method_name, f) in methods:

            def wrapper(*args, **kwargs):
                print('wrapped')
                return f(*args, **kwargs)

            setattr(self, method_name, wrapper)


b = B()

b.foo()
>>> wrapped
>>> foo

b.bar()
>>> wrapped
>>> foo

 

Upvotes: 1

Views: 110

Answers (1)

Axe319
Axe319

Reputation: 4365

This is a spin on a common python gotcha, late binding closures.

What is happening is the last value of f is being bound to all your wrapped methods.

A common workaround is binding your changing variable to a keyword argument or using functools.partial. For your example you can use it as a keyword argument.

class A:
    def foo(self, baz='foo'):
        print(baz)

    def bar(self, baz='bar'):
        print(baz)


class B(A):
    def __init__(self):
        super().__init__()
        methods = [
            (method_name, getattr(self, method_name)) for method_name in dir(self) if not method_name.startswith('_')
        ]

        for (method_name, f) in methods:
            # here you can use an implied private keyword argument
            # to minimize the chance of conflicts
            def wrapper(*args, _f=f, **kwargs):
                print('wrapped')
                return _f(*args, **kwargs)

            setattr(self, method_name, wrapper)


b = B()
b.foo()
b.foo('baz')
b.foo(baz='baz')
b.bar()

I added a few more calls to your method to demonstrate that it still works with different forms of calls.

Upvotes: 1

Related Questions