一、闭包
在上面我们见过了再函数中调用函数本身。那么在函数中可不可以定义一个函数。
问题需求:如何函故外部调用函数内部定义的函数?
问题的引入: ,到底什么是闭包?
闭包的概念:
一个完整的闭包须满足一下三个条件:
1.函数中嵌套一个函数
2.外层函数返回内层嵌套函数的变量名
3.内层函数对外部作用域有一个非全局的变量进行引用
函数内部是否可以定义函数?
可以
def fun():
print("fun")
def fun2():
print("fun2")
函数内部的函数是否能够进行调用?
不行
如何来实现对内部函数的调用?
通过return来返回内部函数名,代码如下:
def fun():
print("fun")
def fun2():
print("fun2")
return fun2
如何来实现内嵌函数的调用?
# 方法一fun()()
#方法二
res = fun()
res()
参考闭包的条件,不满足第三点“内层函数对外部作用域有一个非全局的变量进行引用”,加入非全局变量。
最简单的闭包案列
def fun():
x = 100
print("fun")
def fun2():
print("fun2")
print(x)
return fun2
#接下来我们对上面这个闭包进行分析,看是否满足我们说的三个条件
#第一个条件:函数中嵌套一个函数: fun中嵌套了fun2
#第二个条件:外层函数返回内存函数的变量名: fun中返回了fun2
#第三个条件:内层函数对外部作用域有一个非全局的变量进行引用:fun2中引用了fun中的变量x,而x是fun2的外部作用域fun中的局部变量
综上分析:闭包的三个条件都满足,那么确定是闭包无疑了。
带参数的闭包
def func_a(fun):
print("func_a被调用了")
def func_w():
print("func_w被调用了")
fun()
#与我们前面的闭包函故相比。有什么区别?
#是不是多了个参数?这是一个有参数的闭包。那么装饰器又是什么呢?
#我们在上面定义的闭包函数传入的参数,在后面有调用,那么这个参数是不是一个函数?
#接下来我们定义一个函数
def func_demo() :
print'执行功能开始吃饭'
#这个时候我们调用上面的闭包函数func_a(func_demo)
I
#然而内部函数并没有被调用,那么怎么办,是不是用一个变量来接收一下,然后再调用
func_demo2= func_a(func_demo)
func_demo2()
作用和原理
作用:实现数据锁定,提高稳定性 装饰器
原理:python在运行到函数时,会为函数划分一块地址,用于存储函数,内部嵌套的函数也在该地址中,引用的局部变量会记录到__closure__中,相当于使用的是自身的属性,不受外部影响,也不影响外部变量。
装饰器
讲装饰器之前找们先来了解一下开放封闭原则(面向对象原则的核心)
开放封闭原则︰软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。
装饰器的作用:在不更改原功能函数内部代码,并且不改变调用方法的情况下为原函数添加新的功能。
装饰器的应用场景:
1.登录验证
2.函数运行时间统计
3.执行函数之前做准备工作
4.执行函故后清理功能
5.1、实现一个装饰器
在上面的过程中其实我们已经完成了一个装饰器定义并应用,只是现在的语法并不是按照整个的装饰器来写的,装饰器之要在上面的基础稍加改动一点点就可以了。
假设有一个网站的页面,我现在已经定义好了index页面的调用方法,代码如下:
def index():
print('This is the index page')
在基于开放封闭原则的基础上,我不再修改原有的函数内容,如何对原函数进行功能的拓展,例如我需要登录后才能进行index访问,首先我们要定义一个login函数:
#定义登陆的装饰器方法:
def login(func):#传入需要装饰的函数
def fun():
username = 'joker'
password = 'python'
user = input('请输入账号')
pwd = input('请输入密码')
if user == username and pwd == password:
print('登陆成功')
func()#验证通过后调用原函数
else:
print('账号或者密码错误')
return fun
在原函数的基础上加上装饰器,效果如下:
@login
def index():
print('This is the index page')
index()
当调用index()方法时会先去调用login()方法,通过装饰器实现功能的扩充。
@login:语法糖 等价于 index = login(index)
即把index当做参数传给login,并将return的fun用index进行接收,原来的index被保存在closure中(定义的fun的closure,实际的index的closure,即原来的index方法被保存到了login方法的带吗块中)。
在最后调用index时,实际上执行的是login(index)()。
5.2、实现有参数的装饰器
def add(func):
def fun(a,b):
print('实现装饰器功能')
print('相乘:',a * b)
func(a,b)
return fun
@add
def add_num(a,b):
print('相加:',a + b)
add_num(11,22)
参数的传递过程如下add_num= add(add_num)
1.在调用add_num时,实际上调用的是定义的fun函数,如果fun不进行参数定义,那么程序就会报错,不需要参数,但是传入了两个参数。
2.设置fun的传入参数之后,fun往下进行执行,执行到func()时(func实际上就是之前定义的add_num函数,定义时需要传入两个参数),如果参数不进行传入,程序也会进行报错,程序需要两个参数,但是没有进行参数的传入。
11和22两个参数,首先传递给了内置函数fun,fun接收后再传给传入的函数func。
5.3、实现一个通用装饰器
希望装饰器既可以用于装饰没有参数的函数,也可以装饰带参数的函数
def tongyong(func):
def fun(*args,**kwargs):
print('实现装饰器功能')
func(*args,**kwargs)
return fun
@tongyong
def index():
print('这是初始页面')
@tongyong
def goods_list(n):
print('这是第{}页商品页面'.format(n))
index()
print("-"*30)
goods_list(7)
传参带*号主要做解包的操作。
5.4、实现一个类装饰器
def classzhuangshi(func):
def fun(*args,**kwargs):
print('实现装饰器功能')
return func(*args,**kwargs)
return fun
@classzhuangshi
class MyClass():
def __init__(self):
pass
m = MyClass()
print(m)
关键点在于内置函数要做一个return操作,当没有return时,默认会返回None类型,无法获取到创建的实例对象信息。
前面我们是用闭包函数来实现的装饰器,那么接下来给大家扩展一下,使用类来当做一个装饰器来用如果要把类当做一个装饰器来用,有两步操作,
首先在__init__—方法中,把被装饰的函数赋值给一个实例属性。
然后再了类里面实现一个__call__方法,在call方法中调用原来的函数。
class Test(object):
def __init__(self,func) :
self.func = func
def .__call__(self,*args,**kwargs ):
print('这个是类装饰器')
self.func(*args,**kwargs)
#说明:
#1,当用Test来装作装饰器对func函数进行装饰的时候,首先会创建Test的实例对象,并且会把func这个函数名当做参数传递到__init__方法中,即在__init__方法中的func变量指向了func函数体
#2. func函数相当于指向了用Test创建出来的实例对象
#3.当在使用func()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法
#4.为了能够在__call__方法中调用原来func指向的函数体,所以在__init__—方法中就需要一个实例属性来保存这个函数体的引用,所以才有了
self.__func = func
这句代码,从而在调用__call__方法中能够调用到func之前的函数体。
多个装饰器装扮同一个函数。
@counttime #index = counttime(index) 此时传入的index就是check,赋值以后的index就是fun
@login_check # index = login(index) 此时的index就是check
def index():
print('index')
实际的运行结果如下:
这个是时间函数
1624455280.1388054
这个是登陆函数
账号:joker
密码:python
index
1624455290.8447
耗时为.{} 10.705894708633423
从下往上装饰,从上往下执行。
常用的三个内置类函数
class MyClass():
def __init__(self,name):
self.name = name
@classmethod #类方法,类可直接进行调用,对象也可以进行调用,传参self 变成cls
def test(cls):
print(cls)
@staticmethod #静态方法,不需要进行传参self,实例和类都可以调用
def static():
print('This is static method')
@property #设置只读属性
def setattr(self):
self.age = '18'
return self.age
MyClass.test()
t = MyClass('joker')
age = t.setattr
print(age)
t.age = 25
print(t.setattr)
print(t.age)
输出结果如下:
<class '__main__.MyClass'>
18
18
18