闭包函数
- 闭函数指的是在一个函数内部的函数,即嵌套在函数内部的函数。
- 包函数指的是内部函数对外层函数(非全局作用域)作用域名字的引用。
- 闭包函数基于函数对象,可以将函数返回到任意位置调用,但是作用域的关系在定义函数的时候就被确定了,与函数的调用位置无关。如果内嵌函数包含对外部函数作用域中名字的引用,该内嵌函数就是闭包函数。
# -*- coding: utf-8 -*-
def outer():
name = 'shisbuyu'
def inner():
print('外层函数的内层函数inner', name) # 内层函数使用到了外层函数名称空间中名字
inner()
return inner
res = outer() # 得到的返回值是函数名inner
# 输出
# 外层函数的内层函数inner shisbuyu
闭包函数作用
- 函数体传参方式之 形参
def index(username):
print(username)
index('shisbuyu') # 函数体代码需要什么就可以在形参里写什么
- 函数体传参方式之 闭包
def outter():
name = 'shisbuyu'
def index():
print(name) # name写死了, 调用res() name shisbuyu
return index
res = outer()
res()
做成闭包函数通过形参传参给外层
def outter(name):
def index():
print(name)
return index
res = outter('shisbuyu')
res() # shisbuyu
res1 = outter('shisuiyi')
res1() # shisuiyi
装饰器
装饰器可以在不改动原函数代码的情况下,添加其原本没有的功能。简单点说,就是 修改其它函数的功能的函数 。通过使用装饰器,我们可以让一个函数的功能变的更加强大,还可以让我们的代码更加简短整洁。
普通装饰器
方式一:不使用语法糖@符号的调用方法:
# -*- coding: utf-8 -*-
# 原函数
def one():
print("这是一个平平无奇的函数")
# 定义1个装饰器
def loop(func):
def wrapper(*args, **kw):
for i in range(2):
func(*args, **kw)
return wrapper
loop(one)()
# 输出
这是一个平平无奇的函数
这是一个平平无奇的函数
方式二:使用语法糖@符号的调用方法:
# -*- coding: utf-8 -*-
# 定义1个装饰器
def loop(func):
def wrapper(*args, **kw):
for i in range(2):
func(*args, **kw)
return wrapper
@loop
def one():
print("这是一个平平无奇的函数")
one()
# 输出
这是一个平平无奇的函数
这是一个平平无奇的函数
函数带参数
# -*- coding: utf-8 -*-
def loop(func):
def wrapper(*args, **kw):
for i in range(2):
func(*args, **kw)
return wrapper
@loop
def one(num):
for i in range(num):
print("这是一个平平无奇的函数")
one(2)
#输出
这是一个平平无奇的函数
这是一个平平无奇的函数
这是一个平平无奇的函数
这是一个平平无奇的函数
带返回值
# -*- coding: utf-8 -*-
def loop(func):
def wrapper(*args, **kw):
ret = func(*args, **kw)
return ret
return wrapper
@loop
def one(a, b):
return a + b
res = one(1, 2)
print(res)
# 当使用@loop对one()函数装饰以后,one指向了wrapper()函数,而wrapper()函数的返回值是ret,所以one()函数的返回值被ret接收了
#输出
3
实际案例
日志装饰器
# -*- coding: utf-8 -*-
import logging
import os
'''
* %(asctime)s 即日志记录时间,精确到毫秒@breif:
* %(levelname)s 即此条日志级别@param[in]:
* %(filename)s 即触发日志记录的python文件名@retval:
* %(funcName)s 即触发日志记录的函数名
* %(lineno)s 即触发日志记录代码的行号
* %(message)s 即这项调用中的参数
'''
if not os.path.exists('Log.log'):
file = open('Log.log', 'w')
logging.basicConfig(
filename='Log.log',
format="%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s"
)
'''
* @breif: 日志修饰器,为函数添加日志记录服务
* @param[in]: err -> 发生异常时返回的错误信息
* @retval: 加载日志服务的功能函数
'''
def logger(err):
def log(func):
def warp(*args, **kwargs):
try:
result = func(*args, **kwargs)
return result
except Exception as e:
logging.error(e)
return err
return warp
return log
@logger('出错了')
def add(a, b):
return a + b
print(add(1, 2))
#控制台输出
出错了
# Log.log日志显示
2022-12-13 14:11:22,772 - ERROR - 闭包函数.py - warp - 36 - unsupported operand type(s) for +: 'int' and 'str'
自动化测试脚本中处理异常
举个例子,你正在跑一个自动化测试脚本,突然间设备的闹钟响了,而刚好闹钟的界面遮挡住了你要操作的那个按钮,最终导致脚本运行失败了。没办法,你只能关掉闹钟再重新运行一次脚本。
# -*- coding: utf-8 -*-
def test_retry(func):
def run_case_again(*args, **kwargs):
try:
func(*args, **kwargs)
except:
# 如闹铃、断网重试按钮弹出、短信通知等导致失败
if exists(xxxx):
touch(xxx)
try:
func(*args, **kwargs)
print('用例重新执行成功')
except:
print('用例执行失败')
# 其他你想做的操作,比如停止剩余用例的执行
return run_case_again
@test_retry
def test():
wait(xxxx, timeout=15)
© 版权声明
本站文章由不念博客原创,未经允许严禁转载!
THE END