闭包和装饰器

一、闭包

1.闭包

在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,这个使用外部函数变量的内部函数称为闭包

优点:

  1. 闭包可以保存外部函数内的的变量,不会随着外部函数调用完而销毁

但是因为闭包使用了外部函数的变量,所以外部函数的变量没有及时释放,会消耗内存

  1. 闭包可以提高代码的可重用性
2.构成
  1. 在函数内部再定义函数
  2. 内部函数使用了外部函数的变量
  3. 外部函数返回了内部函数

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属性–装饰器方式
  1. @property修饰获取值的方法,表示把方法当作属性使用,当获取属性时会执行下面修饰的方法
  2. @方法名.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
'''
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PCGuo999

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值