Python装饰类中的方法和继承
Posted
技术标签:
【中文标题】Python装饰类中的方法和继承【英文标题】:Python decorating a method in a class and inheritance 【发布时间】:2018-01-16 12:51:48 【问题描述】:我正在为一个 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
对于理解这一点的任何帮助将不胜感激。
【问题讨论】:
您需要将_catch_popups
设为static method。
【参考方案1】:
这里的结构有错误。在类主体中定义的实例方法与类同时创建,因此是未绑定的方法。因此,在创建时,没有 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
【讨论】:
谢谢,这成功了。原来的问题是固定的,但是当我尝试访问 self.popup 时,值与初始化(无)相同,但是当我访问 self.dialog.popup 时,弹出窗口已被捕获。 @AllenMoh 这是意料之中的。您的perform_login
方法在您的对话框类上,因此对话框实例被传递给_catch_popups
创建的实例方法。如果要捕获窗口本身的弹出窗口,则必须为对话框提供对窗口的引用,然后将属性分配给窗口。
@AllenMoh 我添加了一些更新的方法,将这个属性分配给主窗口。
非常感谢您花时间帮助我了解我做错了什么并告诉我正确的方法。使用更新的方法,一切都按我的意愿工作。以上是关于Python装饰类中的方法和继承的主要内容,如果未能解决你的问题,请参考以下文章