一、闭包
1.闭包
在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,这个使用外部函数变量的内部函数称为闭包
优点:
- 闭包可以保存外部函数内的的变量,不会随着外部函数调用完而销毁
但是因为闭包使用了外部函数的变量,所以外部函数的变量没有及时释放,会消耗内存
- 闭包可以提高代码的可重用性
2.构成
- 在函数内部再定义函数
- 内部函数使用了外部函数的变量
- 外部函数返回了内部函数
eg:
def man1(x):
# 在函数内部再定义函数
def marry(y):
# 内部函数使用了外部函数的变量
print("%s想和%s结婚"%(x,y))
# 外部函数返回了内部函数
return marry
ZS = man1("张三")
#即man函数结束后,返回的marry被ZS保存,并且marry仍保存了man函数的参数"张三"
ZS("李四")
ZS("王五")
'''
执行结果:
张三的朋友和李四结婚
张三的朋友的朋友和王五结婚
'''
3.修改闭包内使用的外部变量
在内部函数中直接修改外部函数变量,并没有改变外部函数值,它只是在内部函数内定义了一个局部变量。想要在内部函数修改外部变量,可以使用 nonlocal 关键字来完成。
def man2(x):
# 在函数内部再定义函数
def marry(y):
nonlocal x # 告诉解释器,此处使用的是 外部变量x
# 修改外部变量x
x = x+"的朋友"
# 内部函数使用了外部函数的变量
print(x+"和"+y+"结婚")
# 外部函数返回了内部函数
return marry
ZS = man2("张三")
#即man函数结束后,返回的marry被ZS保存,并且marry仍保存了man函数的参数"张三"
ZS("李四")
ZS("王五")
'''
执行结果:
张三的朋友和李四结婚
张三的朋友的朋友和王五结婚
'''
二、装饰器
1.装饰器
装饰器是不改变原有函数代码和调用方式的情况下增加额外的功能,本质上就是一个闭包函数。闭包函数只有一个参数并且参数是函数类型,这就是装饰器。
# eg:
def add_acting(f):
def acting():
# 函数执行之前
print("出门")
f() # 被修饰的函数
# 函数执行之后
print("回家")
return acting
def run():
print("跑步")
# 使用装饰器来装饰函数,即使用add_acting函数来修饰run函数
run=add_acting(run)
# 执行修饰后的run函数
run()
'''
执行结果:
出门
跑步
回家
'''
2.装饰器的语法糖写法
如果有多个函数都需要添加一个相同的功能,若每次都编写run=add_acting(run)修饰会很麻烦。为了简化这个操作,可以使用语法糖,语法糖的书写格式是: @装饰器名字
,通过语法糖的方式也可以完成对已有函数的装饰
- @add_acting等价于 run= add_acting(run)
- 装饰器是在加载模块时立即执行。
# eg:
def add_acting(f):
def acting():
# 函数执行之前
print("出门")
f() # 被修饰的函数
# 函数执行之后
print("回家")
return acting
# 使用语法糖后:
@add_acting
def run():
print("跑步")
# 执行修饰后的run函数
run()
'''
执行结果:
出门
跑步
回家
'''
3.装饰带有参数的函数
# eg:
def add_acting(f):
def acting(name):
# 函数执行之前
print(name+"出门")
f(name) # 被修饰的函数
# 函数执行之后
print(name+"回家")
return acting
# 使用语法糖后:
@add_acting
def run(name):
print(name+"跑步")
# 执行修饰后的run函数
run("张三")
'''
执行结果:
张三出门
张三跑步
张三回家
'''
4.装饰带返回值的函数
# eg:
def add_acting(f):
def acting(x,y):
# 函数执行之前
print("开始运行:")
return f(x,y) # 被修饰的函数,返回结果值
return acting
# 使用语法糖后:
@add_acting
def add(x,y):
return x+y
# 执行修饰后的add函数
print(add(2,3))
'''
执行结果:
开始运行:
5
'''
5.装饰带有不定长参数的函数
# eg:
def add_acting(f):
def acting(*args,**kwargs):
# 函数执行之前
print("开始运行:")
return f(*args,**kwargs) # 被修饰的函数
return acting
# 使用语法糖后:
@add_acting
def add(*args,**kwargs):
result1,result2=0,0
for i in args:
result1+=i
for j in kwargs.values():
result2+=j
return (result1,result2)
# 执行修饰后的add函数
print(add(2,3,x=1,y=2))
'''
执行结果:
开始运行:
(5,3)
'''
6.通用装饰器–多个函数用一个装饰器
在每个需要装饰的函数前都加上语法糖@装饰器名
# eg:
def add_acting(f):
def acting(*args,**kwargs):
# 函数执行之前
print("开始运行:")
return f(*args,**kwargs) # 被修饰的函数
return acting
# 使用语法糖后:
@add_acting
def add(*args,**kwargs):
result1,result2=0,0
for i in args:
result1+=i
for j in kwargs.values():
result2+=j
return (result1,result2)
@add_acting
def subtract(x,y):
return x-y
# 执行修饰后的add和subtract函数
print(add(2,3,x=1,y=2))
print(subtract(2,3))
'''
执行结果:
开始运行:
(5, 3)
开始运行:
-1
'''
7.多个装饰器一起装饰
先被最近的装饰器装饰,然后在被外面的装饰器装饰,由内到外的装饰
# eg:
def add_acting(f):
def acting(x,y):
# 函数执行之前
print("开始运行1:")
return f(x,y) # 被修饰的函数,返回结果值
return acting
def add_acting2(f):
def acting(x,y):
# 函数执行之前
print("开始运行2:")
return f(x,y) # 被修饰的函数,返回结果值
return acting
# 使用语法糖后:先被add_acting2装饰,后被add_acting装饰
@add_acting
@add_acting2
def add(x,y):
return x+y
# 执行修饰后的add函数
print(add(2,3))
'''
执行结果:
开始运行1:
开始运行2:
5
'''
8.带有参数的装饰器
使用装饰器时,可以传入参数。做法是在装饰器外再包上一个函数,让最外层函数接收参数,返回的是装饰器。但需要注意:@最外层的函数名(参数)
不可以直接:
def 装饰器(f,x)
,因为装饰器只能有一个参数,并且还要是函数类型
# 带参数的装饰器
def select(flag):
def add_acting(f):
def acting(x,y):
if flag==1:
print("开始运行1:")
else:
print("开始运行2:")
print(f(x,y))
return acting
return add_acting
# 添加装饰器时就直接需要加上参数
@select(1)
def add(x,y):
return x+y
# 执行修饰后的add函数
add(2,3)
'''
执行结果:
开始运行1:
5
'''
9.类装饰器
通过定义一个类来装饰函数
-
因为
@装饰器等价于 函数 = 装饰器(函数)
,类似类的实例化,需要__init__
方法初始化,并加入self参数。 -
想要让类的实例对象能够像函数一样进行调用,需要在类里面使用
__call__
方法,把类的实例变成可调用对象(callable)一个类中一旦实现了
__call__
方法,那么这个类创建的对象就是一个可调用对象,可以像调用函数一样调用 -
类装饰器装饰函数功能在
__call__
方法里面进行添加
# eg:
class add_acting(object):
def __init__(self,add):
self.__add=add # 设置成一个私有属性
def __call__(self,*args,**kwargs):
# 添加装饰
print("开始运行:")
self.__add(*args,**kwargs) # 被修饰的函数
@add_acting
def add(*args,**kwargs):
result1,result2=0,0
for i in args:
result1+=i
for j in kwargs.values():
result2+=j
print(result1,result2)
# 执行修饰后的add函数
add(2,3,x=1,y=2)
'''
执行结果:
开始运行:
5 3
'''
三、property属性
property属性是把一个方法当做属性进行使用,这样做可以简化代码使用。
1.定义property属性–装饰器方式
@property
修饰获取值的方法,表示把方法当作属性使用,当获取属性时会执行下面修饰的方法@方法名.setter
修饰设置值的方法,当设置属性时会执行下面修饰的方法
# 装饰器方式的property属性
class Hero(object):
def __init__(self,name,hp):
self.name=name
self.__hp=hp
# 装饰器方式的property, 把hp方法当做属性使用, 表示当获取属性时会执行下面修饰的方法
@property
def hp(self):
return self.__hp
# 把hp方法当做属性使用, 表示当设置属性时会执行下面修饰的方法
@hp.setter
def hp(self, new_hp):
if new_hp < 0:
print("血量值输入非法,hp未改变")
else:
self.__hp = new_hp
HanBing=Hero("寒冰",1500)
print("英雄名:%s,血量:%d"%(HanBing.name,HanBing.hp))
HanBing.hp=500
print("英雄名:%s,血量:%d"%(HanBing.name,HanBing.hp))
HanBing.hp=-500
print("英雄名:%s,血量:%d"%(HanBing.name,HanBing.hp))
'''
执行结果:
英雄名:寒冰,血量:1500
英雄名:寒冰,血量:500
血量值输入非法,hp未改变
英雄名:寒冰,血量:500
'''
2.类属性方式
在类中相当于设置一个类属性值:
类属性=property(获取值方法,设置值方法)
# 类属性方式的property属性
class Hero(object):
def __init__(self,name,hp):
self.name=name
self.__hp=hp
def get_hp(self):
return self.__hp
def set_hp(self, new_hp):
if new_hp < 0:
print("血量值输入非法,hp未改变")
else:
self.__hp = new_hp
# 类属性方式的property属性
hp=property(get_hp,set_hp)
HanBing=Hero("寒冰",1500)
print("英雄名:%s,血量:%d"%(HanBing.name,HanBing.hp))
HanBing.hp=500
print("英雄名:%s,血量:%d"%(HanBing.name,HanBing.hp))
HanBing.hp=-500
print("英雄名:%s,血量:%d"%(HanBing.name,HanBing.hp))
'''
执行结果:
英雄名:寒冰,血量:1500
英雄名:寒冰,血量:500
血量值输入非法,hp未改变
英雄名:寒冰,血量:500
'''