【测试开发】函数高阶-闭包、装饰器
一、闭包
1、闭包的概念
一个完整的闭包必须满足以下条件
- 函数中嵌套一个函数
- 外层函数返回内层函数的变量名
- 内层函数对外部作用域有一个非全局的变量进行引用
2、闭包案例
- 不带参数的
def func():print('------func函数被调用------')num = 100def count_book(): # 函数内部的局部变量的命名空间print(num) # 内层嵌套函数引用了外层的一个非全局变量print('这个是count_book函数')return count_bookres = func()
res()
# print(type(res)) # 返回的是function类
- 带参数的
def func(num):print('------func函数被调用------')def count_book(): # 函数内部的局部变量的命名空间print(num) # 内层嵌套函数引用了外层的一个非全局变量print('这个是count_book函数')return count_bookres = func(100)
res()
# print(type(res)) # 返回的是function类
二、装饰器
- 面向对象原则的核心:开放封闭原则
1、开放封闭原则
软件实体应该是可扩展,而不可修改的,即对扩展是开放的,而对修改是封闭的。
2、装饰器的作用
在不更改原功能函数内部代码,且不改变调用方法的情况下为原函数添加新的功能
3、装饰器的应用场景
- 登录验证
- 函数运行时间统计
- 执行函数之前做准备工作
- 执行函数后清理功能
4、实现一个不带参数的装饰器
# 不带参数的装饰器def login(func): # 闭包函数中的接收的参数就是内部传进去的函数def fun():username = 'wanzi'password = 'youyou'user = input("请输入账号:")pw = input("请输入密码:")if username == user and pw == password:func() # index.__closure__ 传进去的index在closure属性中else:print("账号或密码错误")return fun@login # @login:语法糖 -->index = login(index)
def index():print('这个是网站的首页')index() # 调用index相当于调用fun()# 以下两行代码作用=@login
# index = login(index)
# index()
5、实现一个带参数的装饰器
# 带参数的装饰器def add(func): # 括号内接收的是一个函数def fun(a,b):print('相乘:', a * b)print('相除:', a / b)func(a,b)return fun@add
def add_num(a,b):# 打印两个数相加print('相加:', a+b)add_num(1,2)
三、装饰器装饰类
装饰器装饰类必须写return返回对象,装饰函数不需要
若多个装饰器装饰了同一个函数,则从下往上装饰,从上往下执行
# 装饰器装饰类
def add(func):def fun(*args, **kwargs):print("装饰器的功能代码")return func(*args, **kwargs)return fun@add # MyClass = add(MyClass)
class MyClass:def __init__(self, name, age):self.name = nameself.age = agem = MyClass('丸子', 18)
print(m.name)
print(m.age)
四、内置装饰器
Python中的内置装饰器,均是在类中使用,不能在类外面使用
1、@classmethod
- 类方法不能直接调用实例的方法,被@classmethod装饰后,可以调用
- 类属性、类方法可以被实例调用
- 类方法可以被类调用,也可以被实例属性调用
- 类无法调用实例属性和实例方法
2、@staticmethod 静态方法
不带参数,实例和类都可以调用
class MyTest(object):@classmethod # 被classmethod装饰之后,该方法就是一个类方法def add(cls): # cls代表的是类本身print('add')print(cls)@staticmethoddef static():print('static静态方法')def sub(self): # self代表的是实例本身print('sub')t = MyTest()
t.add()
t.static()
t.sub()
3、@property
一般用于设置只读属性,装饰完后被装饰的方法可以像属性一样被调用
class MyTest(object):def __init__(self, name):self.name = name@property # 设定只读属性 被装饰的方法可以像属性一样被调用def read_attr(self):return '18岁't = MyTest('优优')
t.name = '丸子' # 支持修改
print('name的值:', t.name)
t.read_attr = '19' # 不支持修改 带@property装饰后只能读不能改 防止属性被篡改
print(t.read_attr)