Class 方法作为 python 中的装饰器

Class method as decorator in python

所以,我正在为 appium 测试编写一个库。 我有一个主 class 看起来像这样:

class APP():
    def __init__(self):
        self.variable1 = 1
        self.current_view = "main_screen"

    def do_operation_A(self):
        self.open_side_menu()
        do_something
        self.current_view = "side_menu"
    
    def do_operation_B(self):
        self.open_side_menu()
        do_something_else
        self.current_view = "side_menu"

    def set_landscape(self):
        self.open_settings_menu()
        configure_landscape
        self.current_view = "settings_menu"

class 有很多操作,所以我可以做 app.do_operation_A() 或 app.set_landscape() 之类的事情,而不必先手动进入每个菜单(在class)

为了减少这种情况,如果可能的话,我想实现一个装饰器来做这样的事情:

class APP():
    def __init__(self):
        self.variable1 = 1
        self.current_view = "main_screen"

    #DEFINE_DECORATOR_HERE

    @side_menu
    def do_operation_A(self):
        do_something
    
    @side_menu
    def do_operation_B(self):
        do_something_else

    @settings_menu
    def set_landscape(self):
        configure_landscape

所以我想实现这个装饰器以导航到相应的视图并更改我用来检查其他函数中的某些内容的变量。我看过一些 functools.wraps 的例子,但我不清楚如何在 class 中实现装饰器来修改这个自变量。

有什么帮助吗?

所以装饰器基本上是一个 returns 另一个函数的函数,对吧?

def side_menu(func):
    def wrapper():
        return func()
    
    return wrapper

side_menu 返回的 wrapper 将在调用 App().do_operationA 时调用。每当调用该方法时,self 始终是第一个参数。或者更确切地说,第一个参数是 App 的实例,但无论如何。所以我们可以这样做:

def side_menu(func):
    def wrapper(self, *args, **kwargs):
        self.open_menu()
        func(self, *args, **kwargs)

    return wrapper

现在,您不希望该方法将自身显示为 wrapper - 您喜欢名称 do_operationA。这就是 @functools.wraps 的用武之地,它使装饰时看起来和工作正常。

def side_menu(func):
    @functools.wraps
    def wrapper(self, *args, **kwargs):
        self.open_menu()
        func(self, *args, **kwargs)

    return wrapper

使用装饰器意味着您“包装”了您的其他函数,即您调用装饰器,然后从装饰器内部调用该函数。

例如:

import functools

def outer(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        return func(*args, **kwargs)
    return inner

定义函数后,装饰器将被调用,返回inner函数。

无论何时调用 func,实际上都会调用 inner,它会运行自己的代码,包括调用原始 func 函数。

因此对于您的用例,您应该能够创建类似于以下内容的装饰器:

def settings_menu(func):
    @functools.wraps(func)
    def inner(self, *args, **kwargs):
        self.open_settings_menu()
        self.current_view = "settings_menu"
        return func(self, *args, **kwargs)
    return inner