网站建设方案是什么,网页设计制作网站代码html,wordpress前端可视化编辑,微信公众号推文模板素材Python 装饰器简介
装饰器#xff08;Decorator#xff09;是 Python 非常实用的一个语法糖功能。装饰器本质是一种返回值也是函数的函数#xff0c;可以称之为“函数的函数”。其目的是在不对现有函数进行修改的情况下#xff0c;实现额外的功能。
在 Python 中#xf…Python 装饰器简介
装饰器Decorator是 Python 非常实用的一个语法糖功能。装饰器本质是一种返回值也是函数的函数可以称之为“函数的函数”。其目的是在不对现有函数进行修改的情况下实现额外的功能。
在 Python 中装饰器属于纯粹的“语法糖”不使用也没关系但是使用的话能够大大简化代码使代码更加简洁易读。
最近在霍格沃兹测试学院的《Python 测试开发实战进阶》课程中学习了 App 自动化测试框架的异常处理存在一定重复代码正好可以当作题材拿来练习一下装饰器。
装饰器学习资料推荐参考 RealPython realpython.com/primer-on-p… 本文主要汇总记录 Python 装饰器的常见踩坑经验列举报错信息、原因和解决方案供大家参考。
装饰器避坑指南 坑 1Hint: make sure your test modules/packages have valid Python names. 报错信息
test_market.py:None (test_market.py)
ImportError while importing test module D:\project\Hogwarts_11\test_appium\testcase\test_market.py.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
test_market.py:9: in modulefrom test_appium.page.app import App
..\page\app.py:12: in modulefrom test_appium.page.base_page import BasePage
..\page\base_page.py:16: in modulefrom test_appium.utils.exception import exception_handle
..\utils\exception.py:11: in modulefrom test_appium.page.base_page import BasePage
E ImportError: cannot import name BasePage from test_appium.page.base_page (D:\project\Hogwarts_11\test_appium\page\base_page.py)原因
exception.py 文件和 base_page.py 文件之间存在相互调用关系。
解决方案
把循环调用的包引入信息放在函数内。只要一方的引用信息放在函数里即可不必两边都放。
我只在 exception.py 文件里改了base_page.py 保持不变。
exception.py
def exception_handle(func):def magic(*args, **kwargs):# 防止循环调用报错from test_appium.page.base_page import BasePage# 获取BasePage实例对象的参数self这样可以复用driver_self: BasePage args[0]
...坑 2IndexError: tuple index out of range 报错信息
test_search.py:None (test_search.py)
test_search.py:11: in modulefrom test_appium.page.app import App
..\page\app.py:12: in modulefrom test_appium.page.base_page import BasePage
..\page\base_page.py:52: in moduleclass BasePage:
..\page\base_page.py:74: in BasePagedef find(self, locator, keyNone):
..\page\base_page.py:50: in exception_handlereturn magic()
..\page\base_page.py:24: in magic_self: BasePage args[0]
E IndexError: tuple index out of range原因
第一次写装饰器真的很容易犯这个错一起来看下哪里写错了。
def decorator(func):def magic(*args, **kwargs):_self: BasePage args[0]...return magic(*args, **kwargs)# 这里的问题不应该返回函数调用要返回函数名称return magic() 为什么返回函数调用会报这个错呢
因为调用 magic() 函数的时候没有传参进去但是 magic() 里面引用了入参这时 args 没有值自然就取不到 args[0] 了。
解决方案
去掉括弧就好了。
def decorator(func):def magic(*args, **kwargs):_self: BasePage args[0]...return magic(*args, **kwargs)# 返回函数名即函数本身return magic坑 3异常处理只执行了1次自动化无法继续 报错信息
主要是定位元素过程中出现的各种异常NoSuchElementException、TimeoutException等常见问题。
原因
异常处理后递归逻辑写得不对。return func() 执行了 func()跳出了异常处理逻辑所以异常处理只执行一次。
正确的写法是 return magic()。
感觉又是装饰器小白容易犯的错误 …emmm…. :no_mouth:
解决方案
为了直观已过滤不重要代码异常处理逻辑代码会在文末放出。
def exception_handle(func):def magic(*args, **kwargs):_self: BasePage args[0]try:return func(*args, **kwargs)# 弹窗等异常处理逻辑except Exception as e:for element in _self._black_list:elements _self._driver.find_elements(*element)if len(elements) 0:elements[0].click()# 异常处理结束递归继续查找元素 # 这里之前写成了return func(*args, **kwargs)所以异常只执行一次return magic(*args, **kwargs)raise ereturn magic坑 4如何复用 driver 问题
自己刚开始尝试写装饰器的时候发现一个问题。
装饰器内需要用到 find_elements这时候 driver 哪里来还有 BasePage 的私有变量 error_max 和 error_count 怎么获取到呢创建一个 BasePage 对象然后通过 func 函数来传递 driver
func 的 driver 是私有的不能外部调用事实证明可以emmm…。
我尝试把异常相关的变量做成公共的没用还是无法解决 find_elements 的调用问题。
解决方案
思寒老师的做法是在装饰器里面创建一个 self 变量取 args[0]即函数 func 的第一个入参self。
_self: BasePage args[0] 这一简单的语句成功解答了我所有的疑问。
类函数定义里面 self 代表类自身因此可以获取 ._driver 属性从而调用 find_elements。 坑 5AttributeError 找到元素后准备点击的时候报错
报错信息
EINFO:root:(id, tv_search)
INFO:root:None
INFO:root:(id, image_cancel)
INFO:root:(id, tv_agree)
INFO:root:(id, tv_search)
INFO:root:Nonetest setup failed
self test_appium.testcase.test_search.TestSearch object at 0x0000018946B70940def setup(self):self.page App().start().main().goto_search()test_search.py:16:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _self test_appium.page.main.MainPage object at 0x0000018946B70780def goto_search(self):self.find(self._search_locator).click()
E AttributeError: NoneType object has no attribute click..\page\main.py:20: AttributeError原因
看了下 find 函数找到元素后有返回元素本身。 exception_handledef find(self, locator, keyNone):logging.info(locator)logging.info(key)# 定位符支持元组格式和两个参数格式locator locator if isinstance(locator, tuple) else (locator, key)WebDriverWait(self._driver, 10).until(expected_conditions.visibility_of_element_located(locator))element self._driver.find_element(*locator)return element那就是装饰器写得不对了
def exception_handle(func):def magic(*args, **kwargs):_self: BasePage args[0]try:# 这里只是执行了函数但是没有returnfunc(*args, **kwargs)# 弹窗等异常处理逻辑except Exception as e:raise ereturn magic解决方案
要在装饰器里面返回函数调用要不然函数本身的返回会被装饰器吃掉。
def exception_handle(func):def magic(*args, **kwargs):_self: BasePage args[0]try:# return函数执行结果return func(*args, **kwargs)# 弹窗等异常处理逻辑except Exception as e:raise ereturn magic思考
写装饰器的时候各种return看着有点头晕。每个函数里面都可以return分别代表什么含义呢
def exception_handle(func):def magic(*args, **kwargs):_self: BasePage args[0]try:# 第1处 return传递func()函数的返回值。如果不写原有return则失效return func(*args, **kwargs)# 弹窗等异常处理逻辑except Exception as e:for element in _self._black_list:elements _self._driver.find_elements(*element)if len(elements) 0:elements[0].click()# 异常处理结束递归继续查找元素 # 第2处 return递归调用装饰后的函数。magic()表示新函数func()表示原函数不可混淆return magic(*args, **kwargs)raise e# 第3处 return返回装饰后的函数装饰器语法。不能返回函数调用magic()return magic装饰器完整实现
exception.py
import logginglogging.basicConfig(levellogging.INFO)def exception_handle(func):def magic(*args, **kwargs):# 防止循环调用报错from test_appium.page.base_page import BasePage# 获取BasePage实例对象的参数self这样可以复用driver_self: BasePage args[0]try:# logging.info(error count is %s % _self._error_count)result func(*args, **kwargs)_self._error_count 0# 返回调用函数的执行结果要不然返回值会被装饰器吃掉return result# 弹窗等异常处理逻辑except Exception as e:# 如果超过最大异常处理次数则抛出异常if _self._error_count _self._error_max:raise e_self._error_count 1for element in _self._black_list:# 用find_elements就算找不到元素也不会报错elements _self._driver.find_elements(*element)logging.info(element)# 是否找到弹窗if len(elements) 0:# 出现弹窗点击掉elements[0].click()# 弹窗点掉后重新查找目标元素return magic(*args, **kwargs)# 弹窗也没有出现则抛出异常logging.warning(no error is found)raise ereturn magic一点学习心得
“纸上得来终觉浅绝知此事要躬行”。遇到问题后尝试自主解决这样踩过的坑才印象深刻。
所以建议大家最好先根据自己的理解写一遍装饰器遇到问题实在没有头绪了再参考思寒老师的解法那时会有一种豁然开朗的感觉这样学习的效果最好。
以上Python 装饰器踩到的这些坑如有遗漏欢迎补充~
最后感谢每一个认真阅读我文章的人礼尚往来总是要有的虽然不是什么很值钱的东西如果你用得到的话可以直接拿走 这些资料对于【软件测试】的朋友来说应该是最全面最完整的备战仓库这个仓库也陪伴上万个测试工程师们走过最艰难的路程希望也能帮助到你