如何使用父类装饰器实现与实例相关的功能?

时间:2018-12-07 20:20:36

标签: python decorator python-decorators

我想在我的子类中实现一个父类装饰器,其功能取决于子类实例的状态。我尝试从三个不同的角度来解决这个问题,但没有一个起作用:


Parent method

如果give_feedback是静态方法,则该方法内没有self。但是,如果它是一个实例方法,则应用它的名称空间中将没有self

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__(请参阅链接example_1)

无法从__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(请参阅链接example_2)

可以访问对象,但不能访问参数。

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.

有什么好方法吗?

1 个答案:

答案 0 :(得分:0)

为什么不结合第二种方法和第三种方法?使用__get__获取类实例,并使用__call__echo装饰。而不是返回app_func,而是返回一个保存实例并具有所需的__call__行为的新对象。

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)