跳到主要内容

Python 装饰器函数

装饰器说明

🏀装饰器(Decorators)是 Python 的一个重要部分。

简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短, 也更Pythonic(Python范儿)。

装饰器完美完美发挥了动态语言的灵活性, 如果你没使用过python的装饰器, 你可能对python装饰器的函数感到困惑, 因为这跟其它如java, js, lua计算机语言语法差别很大, 如在java中, @开头的代码一般是注解(annotations)。请先学习相关内容。

在自动化领域, 借助装饰器函数🎊, 我们可以使我们的脚本逻辑更清晰更易于维护🔧!我们在示例工程中提供了相关的实用装饰器函数封装例子。

一个最简单的装饰器

from functools import wraps, partial

def run(func):
@wraps(func)
def run_func(*args, **kwargs):
return func(*args, **kwargs)
return run_func()

@run
def task():
print("run task")

### 输出: run task

上述代码中, 我们定义了一个task函数, 我们并没有使用task(), 但是这个task函数却可以被装饰器直接运行。

一个代码DEMO

一个使用yydsfun装饰器的例子

@yydsfun.register_task("百度浏览器")
def task_for_bd():
sleep(5) and ocr_click_any("跳过", h=0.2) or sleep(5) and ocr_click_any("跳.", h=0.2)
click_search_edit = lambda: ocr_click_if_found("搜索", h=0.2, x=0.7, offset_w=-.6) or click(
*scal_pos_1080_2400(659, 156))
is_search_or_home = lambda: screen_find_image_x(("img/百度立即领钱.jpg",), y=0.8, x=0.3, w=0.3, threshold=0)
is_search_result_page = lambda: screen_find_image_x(("img/百度浏览器搜索栏.jpg", "img/百度浏览器搜索栏2.jpg"),
h=0.2, threshold=0)

def try_go_home_or_search():
ocr_exists_any("登录领钱", y=0.5, h=0.3) and click(*scal_pos_1080_2400(128, 2332))
ocr_click_any("禁止", "去设置", "仅在使用中允许", "始终允许", "关闭", y=0.45)
ocr_click_any("全部", y=0.3, h=0.15)
find_image_click("img/百度浏览器全部.jpg", h=0.2, w=0.15, threshold=0)

for _ in range(4):
if is_search_or_home() or false_sleep(4) or is_search_or_home():
break
else:
toast_print("当前没有领钱图标, 可能在其它页面, 执行返回!")
key_back()
sleep(1)

# 确保当前是否在相对的Activity, 才执行指定函数
@yydsfun.run_activity_handler("com.baidu.searchbox.MainActivity", "com.baidu.browser.search.LightSearchActivity")
def handle_home():
times = random.randint(4, 9)
for i in range(times):
ocr_click_if_found("同意", y=0.55, x=0.2, w=0.4)
screen_find_image_x(("img/百度浏览器立即提现.jpg", "img/百度浏览器新用户专属.jpg"), min_prob=0.8,
threshold=0) and click(
*scal_pos_1080_2400(842, 819))
toast_print(f"熊掌阅读 {i}/{times}")
ocr_click_any("禁止", "去设置", "仅在使用中允许", "关闭", y=0.45)
swipe_up()
sleep(1.5)
swipe_up()
try_go_home_or_search()
random_sleep(3, 8)

toast_print("熊掌阅读完毕, 开始输入搜索")
click_search_edit()


@yydsfun.register_task("小米浏览器")
def task_for_xm():
click_search = lambda: click(*scal_pos_1080_2400(959, 164))
click_search_edit = lambda: click(*scal_pos_1080_2400(531, 158))
is_in_home_page = lambda: screen_find_image_x(("img/小米浏览器免费小说我的.jpg",), y=0.8, x=0.2, threshold=0,
min_prob=.8)
is_search_result = lambda: screen_find_image_x(("img/小米浏览器搜索.jpg",), y=50, h=0.2, x=0.7, threshold=0)

def try_go_search():
for _ in range(5):
if is_search_result() or false_sleep(4) or is_search_result():
return
else:
toast_print("当前不在搜索结果页, 执行返回")
if ocr_exists_any("主页", "视频", "窗口", "菜单", "我的", "免费小说", y=0.85):
click(*scal_pos_1080_2400(635, 143))
toast("点击搜索栏")
sleep(5)
else:
key_back()
sleep(2)
bring_app_to_top(env.Xm_Liulan_APP.package)

@yydsfun.run_activity_handler("com.android.browser.BrowserActivity")
def handle_home():
ocr_click_if_found("同意", y=0.6, x=0.2, h=0.4) and sleep(2)
if is_in_home_page():
toast("当前在浏览器主页, 开始往下滑动阅读")
times = random.randint(4, 9)
for i in range(times):
toast_print(f"小米浏览器随机阅读 {i}/{times}")
grant_permission()
swipe_up()
random_sleep(3, 8)
toast_print("阅读完毕, 开始输入搜索")

# 执行task_for_bd函数
yydsfun.handle_task("百度浏览器")

# 执行task_for_xm函数
yydsfun.handle_task("小米浏览器")

DEMO的源码分析

在上述代码中, 我们的目的一个针对多应用的脚本, 我们设计一个函数对应一个应用。

为了避免使用类似以下代码以精简代码, 使用@yydsfun.register_task注册任务, 然后我们使用yydsfun.handle_task运行相对应的函数。

# 假如我们有好多应用, 以下代码会变得冗长
if cur_task == "百度浏览器":
task_for_bd()
elif cur_task == "小米浏览器":
task_for_xm()
....
# 上述代码我们可以压缩成一行代码, 提高代码的可维护性
yydsfun.handle_task(cur_task)

为了编写一个稳定的自动化脚本, 也就是不乱点的自动化脚本, 我们需要作出很多判断。在代码中混杂大量的if判断。

@yydsfun.run_activity_handler("Activity1", "Activity2")
def 执行界面任务():
toast("你进入了Activity1 或 Activity2")

上述代码等介于以下代码

activity_name = current_activity()
if activity_name == "Activity1" or activity_name == "Activity2":
执行界面任务()

装饰器: run_no_hurt (运行一个函数并且禁止此函数抛出异常, 如有异常则打印)

装饰器: retry_until_true (循环运行一个函数, 直接函数返回结果为真)

def retry_until_true(retry_time=40, interval=1):
"""
:param retry_time: 最大循环次数
:param interval: 每轮循环间隔秒数
"""
pass