0

I would like to implement a parent class decorator in my child class whose functionality depends on the state of the child class instance. I've tried coming at this problem from three different angles, none of which have worked:


Parent method

If give_feedback is a static method, there's no self within the method. But if it's an instance method, there's no self within the namespace in which it's applied.

class Interface:
    def __init__(self, quiet=False):
        self.quiet = quiet

    def echo(self, text):
        if not self.quiet:
            print(text)

    def give_feedback(self, func):
        def wrapper(*args):
            print('Calling give_feedback.')
            self.echo(func(*args))
        return wrapper

class App(Interface):
    @Interface.give_feedback  # self not defined here.
    def app_func(self, num):
        feedback = 'Success with {num}'.format(num=num)
        return feedback

if __name__ == '__main__':
    a = App()
    a.app_func(3)

Parent class using __call__ (cf. link example_1)

Can't access the object from within __call__.

class Interface:
    # ...

    class give_feedback:
        def __init__(self, func):
            self.func = func

        def __call__(self, *args):
            print(
                'Calling {func}'.format(func=self.func)
            )
            instance = get_obj_instance(self.func)  # What is this?
            return instance.echo(self.func(instance, *args))

class App(Interface):
    # ...

if __name__ == '__main__':
    # ...

Parent descriptor (cf. link example_2)

Can access the object, but no arguments.

class Interface:
    # ...

    class give_feedback:
        # ...

        def __get__(self, instance, owner):
            print(
                'Getting {func} from {inst} of {ownr}'.format(
                    func=self.func, inst=instance, ownr=owner
                )
            )
            num = 2  # How to get num???
            return instance.echo(self.func(instance, num))

class App(Interface):
    # ...

if __name__ == '__main__':
    a = App()
    a.app_func  # No ability to pass parameters.

Is there a good way to do this?

Nathaniel Jones
  • 939
  • 1
  • 14
  • 25

1 Answers1

0

Why not combine the 2nd and 3rd methods? Use __get__ to get the class instance, and __call__ to decorate with echo. Rather than returning app_func, return a new object that holds the instance and has the desired __call__ behavior.

class Interface:
    def __init__(self, quiet=False):
        self.quiet = quiet

    def echo(self, text):
        if not self.quiet:
            print(text)

    class give_feedback:
        def __init__(self, func):
            self.func = func

        def __get__(self, instance, owner):
            return self.InstHolder(instance, self.func)

        class InstHolder:
            def __init__(self, inst, func):
                self.inst = inst
                self.func = func

            def __call__(self, *args):
                return self.inst.echo(self.func(self.inst, *args))


class App(Interface):
    @Interface.give_feedback
    def app_func(self, num):
        feedback = 'Success with {num}'.format(num=num)
        return feedback


if __name__ == '__main__':
    a = App()
    a.app_func(3)
    a.quiet = True
    a.app_func(4)
Nathaniel Jones
  • 939
  • 1
  • 14
  • 25