闭包和装饰器(四)
一、闭包
问题一:函数内嵌套一个函数,在函数外是否可以调用?
答:不可调用
def func(): print("----func----") def wrapper(): print("----wrapper----") return wrapper
注意:外层函数返回的就是内层函数的函数名,若return wrapper(),则已经直接调用了内层函数,那么外层返回的是内层函数的返回结果:None
问题二:什么是闭包?
满足闭包的三个条件:
1.函数中嵌套一个函数
2.外层函数的返回值是内层函数的函数名
3.内层嵌套函数对外部作用域有一个非全局变量的引用
全局变量:模块定义的变量
局部变量:函数内部定义的变量 or 传进函数的参数
闭包的作用:
1.实现数据锁定
def fun2(): num = 100 def wrapper(): print("这里使用了num的值:{}".format(num)) return wrapper wrapper = fun2() num = 1000 wrapper()
运行结果:发现全局变量修改内部变量无效。
二、装饰器(decorator)
开放封闭原则:软件实体应该是可扩展而不可修改。也就是说对扩展是开放的,而对修改是封闭的。
封闭:已实现的功能代码,对内部修改是封闭的。
开放:对功能的扩展是开放的。
装饰器的作用:可以在不修改原功能代码的同时给它扩展新的功能,同时不改变调用方式。
@语法糖
普通装饰器
#装饰器函数(通过闭包实现) def decorator(func):
print("---decorator被调用了---") def wrapper(): print("这个是装饰器函数") func() return wrapper @decorator #作用等于在函数定义之后执行 func = decorator(func) def func(): print("这个是原功能函数func") # func = decorator(func) # @decorator() func()
装饰器执行原理:先执行装饰器函数(@decorator)返回wrapper,再执行wrapper(),在wrapper中,func函数作为装饰器参数func传入,执行原func函数
装饰器常见运用场景:
1.权限校验
2.用来给函数运行计算时间
3.环境准备和恢复的工作
4.web自动化用例失败截图
from selenium import webdriver brower = webdriver.Chrome() brower.get("https://www.baidu.com") def decorator(func): def wrapper(): try: func() except: brower.save_screenshot('error.png') raise return wrapper @decorator def test_search(): brower.find_element_by_xpath("//input[@id = 'kw]") brower.find_element_by_xpath("//input[@id = 'su11']").click()
test_search()
带参数的装饰器
def decorator(func): def wrapper(*args, **kwargs): print('这是个装饰器函数') func(*args, **kwargs) return wrapper @decorator def add_num(a, b): print('计算a+b的结果', a + b) add_num(a=11, b=22) @decorator def func(*args): print(*args) tu =(11,22,33) func(*tu)
类实现装饰器
__call__:魔术方法:在对象使用()调用的时候被触发
class MyDecorator(object): def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print('这个是__call__方法') self.func(*args, **kwargs) @MyDecorator def add_num(): print('计算a+b的结果') add_num()
类装饰的工作流程可以分为两部分:装饰阶段和调用阶段。解释器一遇到“@MyDecorator”语句时就会完成对add_num( )函数的装饰,即调用MyDecorator类的__init__( )方法;当代码执行到add_num( )时,会执行装饰类MyDecorator的__call__( )方法,实现对add_num( )函数的调用。
虽然"@MyDecorator"后面没有括号,也没有参数,但实际上它是有参数的,被装饰的对象add_num()就是@MyDecorator的默认参数,函数对象被传入到装饰器类的构造方法__init__()中,并在__call__()方法中调用。当然我们也可以通过"@MyDecorator(args)"的语法像类装饰器传递其他参数,但是需要值得注意的是:一旦在@MyDecorator()中的参数被传入到__init__()方法,被装饰函数对象add_num()无法同时传入。解决办法:将被装饰函数对象传入__call__()方法。
如下图代码:在MyDecorator类的__call__( )方法中定义了一个内部函数(闭包)接收add_num( )函数中的参数,并实现对被装饰函数的调用。
class MyDecorator(object): def __init__(self, desc): print('__init__') print(desc) def __call__(self, func): print('这个是__call__方法') def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper @MyDecorator('百家号') def add_num(): print('计算a+b的结果') add_num()
执行结果:

参考:https://baijiahao.baidu.com/s?id=1627531125454422806&wfr=spider&for=pc

浙公网安备 33010602011771号