Python 在 class 中修饰一个方法并继承

Python decorating a method in a class and inheritance

我正在为 GUI 应用程序编写测试自动化框架,我想使用装饰器来捕获由 class(例如登录)中的方法生成的弹出窗口

我有一个 _BaseWindow class 跟踪每个 window 中的 GUI 元素(例如:菜单栏、弹出窗口),它由MainWindow class。 MainWindow class 跟踪主菜单上的按钮,以及单击其中一个按钮时生成的对话框。例如,如果您单击主菜单上的登录按钮,将加载登录对话框。

class _BaseWindow(object):
    def __init__(self):
        self.window = "windowName"
        self.popup = None

    def _catch_popups(self, method):
        from functools import wraps
        @wraps(method)
        def wrapper(*args, **kwargs):
            # get a list of the open windows before the method is run
            before = getwindowlist()

            retval = method(*args, **kwargs)

            # get a list of the open windows after the method is run
            after = getwindowlist()

            for window in after:
                if window not in before:
                    self.popup = window
                    break

            return retval
        return wrapper

class MainWindow(_BaseWindow):
    def __init__(self):
        self.dialog = None
        self.logged_in = False

        # buttons
        self.login_button = "btnLogin"

        super(MainWindow, self).__init__()

    def click_login(self):
        if not self.dialog:
            mouseclick(self.window, self.login_button)
            self.dialog = LoginDialog()

class LoginDialog(_BaseWindow):
    def __init__(self):
        # buttons
        self.button_ok = "btnLoginOK"
        self.button_cancel = "btnLoginCancel"
        # text input
        self.input_username = "txtLoginUsername"
        self.input_password = "txtLoginPassword"

        super(LoginDialog, self).__init__()

    @MainWindow._catch_popups
    def perform_login(self, username, password):
        input_text(self.input_username, username)
        input_text(self.input_password, password)
        mouseclick(self.window, self.button_ok)

当我尝试对此进行测试时,我得到:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "objects/gui.py", line 185, in <module>
    class LoginDialog(_BaseWindow):
  File "objects/gui.py", line 236, in LoginDialog
    @MainWindow._catch_popups
TypeError: unbound method _catch_popups() must be called with 
MainWindow instance as first argument (got function instance instead)

当我尝试将 MainWindow instance 指定为:

@MainWindow._catch_popups(self)

我得到:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "objects/gui.py", line 185, in <module>
    class LoginDialog(_BaseWindow):
  File "objects/gui.py", line 236, in LoginDialog
    @MainWindow._catch_popups(self)
NameError: name 'self' is not defined

如果能帮助理解这一点,我们将不胜感激。

您的结构有误。 class 主体中定义的实例方法与 class 同时创建,因此是未绑定方法。因此,在创建时,没有 self 可供参考。您的 _catch_popups 方法需要一个实例。

自然的解决方案是将 _catch_popups 更改为 staticmethod returns 实例方法。

@staticmethod
def _catch_popups(method):
    from functools import wraps
    @wraps(method)
    def wrapper(self, *args, **kwargs):
        before = getwindowlist()
        retval = method(self, *args, **kwargs) # notice that self is passed
        after = getwindowlist()
        try: self.popup = next(w for w in after if w not in before)
        return retval
    return wrapper

这里,self被显式传递给了method,因为它在传递给装饰器时仍然是未绑定的实例方法。


正如您评论中所建议的那样,您想将弹出窗口添加到 window 实例。为此,您可以更新以下方法:

def click_login(self):
    if not self.dialog:
        mouseclick(self.window, self.login_button)
        self.dialog = LoginDialog(self) # pass window to dialog __init__

class LoginDialog(_BaseWindow):
    def __init__(self, parent_window):
        # original code
        self.parent = parent_window

@staticmethod
def _catch_popups(method):
    def wrapper(self, *args, **kwargs):
        # original code
        try: self.parent.popup = ... # add to parent rather than self
        return retval
    return wrapper