8、闭包和装饰器

闭包
闭包需要满足什么条件?(面试常问)
1.函数中嵌套一个函数
2.外层函数的返回值是内嵌函数的函数名
3.内嵌函数对外部作用域有一个非全局变量的引用
a.外层函数定义的变量
b.外层函数的入参
如果内嵌函数引用了一个全局变量,则就不是闭包
闭包的作用
a.闭包可以实现对数据的锁定,提高稳定性。闭包函数比普通函数会多出一个closure属性,其中定义了一个元组来存放多个cell对象,而每个cell对象分别保存了这个闭包中所有的外部变量
b.因为闭包对数据进行锁定,所以里面的属性不存在被修改的可能,所以使用起来更加安全,无需特地去对属性进行私有化
闭包在Python中十分常见,比如说装饰器。当你需要实现一个带参数的装饰器时,这个装饰器函数就是个闭包

闭包的例子

def func(a): # 传入外部变量
def wrapper(): # 1.内嵌函数
print(a) # 2.引用外部变量

return wrapper  # 3.返回内嵌函数的名称

f = func(100) # 这里接收了返回的wrapper函数对象
f() # 相当于执行wrapper()函数,所以会打印出a的值:100
cells = f.closure
print(cells) # (<cell at 0x00000000004EEE58: int object at 0x0000000054EF7870>,)
print(cells[0].cell_contents) # 元组中的第一个cell的值:100
装饰器
在说装饰器之前,首先说下“开放封闭原则”,它可以说是面向对象原则的核心思想
什么是开放封闭原则?
软件实体应该是可扩展,但是不可修改的。函数对扩展是开放的,对修改是封闭的。通俗地讲,就是函数中已经实现的代码是不能修改的,但是可以对函数添加新的功能
为什么要先说“开放封闭原则”呢?因为装饰器就能让开发者很好地遵守这个原则
装饰器的作用
可以在不更改函数的内部代码,甚至不更改函数的调用方法的情况下,对函数进行功能的扩展
函数的类型
1.普通装饰器
2.带参的装饰器
3.通用装饰器
4.类实现装饰器
5.装饰器装饰类
6.三个内置的装饰器
@装饰器在执行当前的py文件时,就已经执行了。并不是其装饰的函数/类被创建时才执行
装饰器常见的应用场景
1.鉴权校验(登录/权限)
2.给函数计算运行时间
3.测试环境的准备和恢复
4.UI自动化测试异常时的截图

1、普通装饰器(闭包实现)
“”" 1. 普通装饰器(闭包实现)"""

def decorator(func): # 可传入函数
def wrapper():
print(‘装饰器函数’)
func() # 调用传入的函数

return wrapper  # 返回内嵌函数

@decorator;相当于 func1 = decorator(func)

@decorator
def func():
print(“原功能函数”)

func()
‘’’
打印结果:
装饰器函数
原功能函数
‘’’

“”“1.1、应用场景:UI自动化报错截图”""
from selenium import webdriver

browser = webdriver.Chrome()
browser.get(‘http://www.baidu.com’)

def screen_shot(func):
def wrapper():
try:
func()
except Exception:
print(‘error’)
browser.save_screenshot(“Error.png”) # 截图

return wrapper

@screen_shot
def search():
browser.find_element_by_xpath(’//[@id=“kw”]’).send_keys(‘海贼王’)
browser.find_element_by_xpath(’//
[@id=“xxx”]’).click() # xpath路径写错

search = search()

接口获取一次token
import requests
class identy_login(object):
def init(self, fun):
self.fun = fun
self.url = ‘http://www.lemonban.com/user/ajax/login’
self.user = ‘******’
self.pwd = ‘******’

def __call__(self, *args, **kwargs):
    res = requests.post(self.url, data={"accout": "xxx", "password": "xxx"})
    cookie = res.cookies
    setattr(self.fun, 'cook', cookie)
    self.fun(*args, **kwargs)
    print("登陆成功")
    return self.fun(*args, **kwargs)

@identy_login
class getloginuser:
def init(self):
pass

def gu(self):
    url = "http://www.lemonban.com/user/ajax/getloginUser"
    response = requests.post(url, cookies=self.cook)
    print(response.text)

if name == ‘main’:
aa = getloginuser()
aa.gu()

带参数的函数和元组拆包
def decorator(func):
print(’---------decorator被调用-------------’)

# 这里要接收参数,使用不定长参数来接收
def wrapper(*args):
    print('这个是装饰器函数')
    # 这里也要接收参数
    func(*args)

return wrapper  # 注意,这里没()

@decorator
def add_num(a, b):
print(‘a+b:’, a + b)

add_num(1,2)

元组拆包

def func1(a, b, c):
print(a, b, c)

tu = (11, 22, 33)

func1(tu) # 会报错

func1(*tu) # *会把元组进行拆包成三个元素,然后当成参数传过去

通用装饰器(就是能接收任意参数的装饰器)
def decorator(func):
print(’---------decorator被调用-------------’)

# 可接收任意形式的参数
def wrapper(*args,  **kwargs):
    print('这个是装饰器函数')
    # 这里也一样
    func(*args, **kwargs)

return wrapper  

@decorator
def add_num(a, b, c):
print(‘a+b+c:’, a + b + c)

add_num(1,2,3)

类实现装饰器
1.类实现装饰器主要是依赖两个魔术方法
a.init:类被初始化时调用它
b.call:在使用类名()时,调用它
class MyDecorator(object):
print(‘MyDecorator类执行’)

def __init__(self):
    print('__init__方法执行')

def __call__(self, *args, **kwargs):
    print('__call__方法执行')

my = MyDecorator() # 打印:MyDecorator类执行 __init__方法执行
my() # 这样使用类对象,就和使用函数对象一样;打印出:这个是__call__方法

类实现装饰器例子
class MyDecorator(object):
print(‘MyDecorator类执行’)

# 接收被装饰函数
def __init__(self, func):
    print('__init__方法执行')
    self.func = func

# 需要被装饰的函数,放在__call__方法中
def __call__(self, *args, **kwargs):
    print('__call__方法执行')
    self.func(*args, **kwargs)  # 调用被装饰函数

@MyDecorator # 1.首先add_num = MyDecorator(add_num(a, b));2.add_num(a, b)被这个类的__init__方法接收
def add_num(a, b):
print(‘a+b:’, a + b)

add_num(2, 4) # 3.执行这里时,MyDecorator类中的__call__方法会被调用

在这里插入图片描述在这里插入图片描述

装饰器装饰类
1.有三种装饰器可以装饰类
a.类装饰器装饰类
b.函数装饰器装饰类
c.非闭包函数装饰类
2.装饰器装饰类,必须返回被装饰类对象的名称【重要】
a.如果是类装饰器,在__call__方法中返回
b.如果是函数装饰器,在内嵌装饰函数中返回
c.如函数装饰器
类装饰器装饰类的例子
3.装饰器装饰类或函数,不仅可以对功能进行扩展,还可以对其添加新的属性(变量)
a.可通过setattr(func, key, value)进行添加,func是函数/方法,key是属性名称,value是属性值
“”“类装饰器装饰类”""
class MyDecorator(object):

def __init__(self, func):
    print('__init__方法开始执行')
    self.func = func

def __call__(self, *args, **kwargs):
    print('__call__方法开始执行')
    func = self.func()  # 被装饰函数被调用
    return func  # 需要返回被装饰类,否则返回None,那样就无法调用被装饰类中的方法

@MyDecorator # Hero = MyDecorator(Hero),初始化MyDecorator,执行它的__init__方法
class Hero(object):
def func(self):
print(‘Hero的func方h法’)

h = Hero() # 执行MyDecorator的__call__方法,并返回被装饰后的Hero对象
h.func() # 由于返回了Hero对象,所以能调用它的方法
在这里插入图片描述
在这里插入图片描述
函数装饰器装饰类的例子(对属性进行扩展)
“”“函数装饰器装饰类”""

def decorator(func):
print(’---------decorator被调用-------------’)

def wrapper(*args, **kwargs):
    print('这个是装饰器函数')
    setattr(func, 'name', '被添加的属性name')  # 可以给被装饰对象添加属性
    obj = func(*args, **kwargs)
    return obj  # 装饰器装饰类,需要把对象返回去,不然返回值是None

return wrapper  # 注意,这里没()

@decorator # 1.把类当成参数;Hero = decorator(Hero)
class Hero(object):
def func2(self):
print(’—func2----’)

h = Hero() # 2.接收装饰过Hero类的wrapper()函数,并且wrapper()函数返回的是装饰后的Hero类
h.func2() # 3.因为返回了Hero类,所以能调用Hero的函数
print(h.name) # 4. 打印:被添加的属性name
在这里插入图片描述
非闭包函数装饰器装饰类
使用这种方式,在使用装饰器@xxx时,就已经调用了装饰器扩展的新功能(闭包是在调用时才调用)
所以,这种方式一般是用于给类添加新属性时使用;ddt就是这样做的
“”"
非闭包函数装饰类
“”"

def decorator1(cls):
setattr(cls, ‘name’, ‘装饰器加入的name属性’)
print(‘装饰器扩展的新功能’)
return cls

@decorator1 # 把类当成参数;Hero = decorator1(Hero),并执行了扩展的新功能
class Hero(object):
def func2(self):
print(’—func2----’)

h = Hero()
print(h.name) # 装饰器加入的name属性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值