Python在类和继承中装饰方法

时间:2017-08-08 19:53:15

标签: python class inheritance decorator class-method

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

我有一个_BaseWindow类,用于跟踪每个窗口中的GUI元素(例如:菜单栏,弹出窗口),这些元素由MainWindow类继承。 MainWindow类跟踪主菜单上的按钮,以及单击其中一个按钮时生成的对话框。例如,如果单击主菜单上的登录按钮,则会加载登录对话框。

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

理解这一点的任何帮助都将非常感激。

1 个答案:

答案 0 :(得分:3)

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

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

@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,因为它在传递给装饰器时仍然是未绑定的实例方法。

根据评论中的建议,您希望将弹出窗口添加到窗口实例中。为此,您可以更新以下方法:

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