【玩转python】入门篇day15-闭包、装饰器

1、闭包

形成闭包的条件

  1. 必须是函数嵌套函数
  2. 内部函数必须引用一个定义在闭合范围外的外部函数的变量
  3. 外部函数必须返回内部函数

代码示例

# 外部函数
def outer1(b):
    a = 10
    # 内部函数
    def inner1():
        # 在内部函数中使用外部函数的变量
        print(a + b)
    return inner1   # 将定义的内部函数返回

func1 = outer1(98)
# func1()  # 108

2、装饰器

Python中的装饰器(Decorator)是一个高级且非常有用的功能,它允许我们在不修改原有函数代码的情况下,给函数增加新的功能。

  1. 装饰器也是一个闭包
  2. 装饰器本质上是一个函数,它接收一个函数作为参数并返回一个新的函数。
  3. 这个新的函数通常是对原函数的一个封装,即在新函数内部调用原函数,并可以在调用前后添加新的功能。、

在代码的运行的期间,可以动态的给原函数增加功能,被称为装饰器(使用闭包函数实现)好处:在团队开发中,假如两个或者更多的同事使用了相同的功能,但是整体的效果又有一些细微的差别,这时候可以采用装饰器,互相不影响.代码简化。

代码演示装饰器

#首先外部函数接收的是一个函数
def outer(fn):
    def inner():
        '''
            公共的处理逻辑 区域
        '''
        fn()#内部函数调用
    #返回内部函数
    return inner  

调用

def demo():
    print('外部不同需求实现的不同的功能')
demo = outer(demo)
demo()#输出 “公共的处理逻辑 外部不同需求实现的不同的功能”

分析

在团队开发中,假如两个或者更多的同事使用了相同的功能,但是整体的效果又有一些细微的差别,这时候可以采用装饰器,互相不影响.代码简化,看上面的例子 整体功能实现放在“公共的处理逻辑 区域”,这时候不同的人可能会有不同的实现差异,然后我们就可以根据不同的人定义不同的demo函数,调用的时候执行demo = outer(demo),调用outer把demo函数传进去,的到的是inner函数,然后再执行demo(),相当于执行inner,这个时候就同时执行了公共处理区域的代码逻辑,也执行了自定义的demo逻辑,实现了给函数添加功能。

简写

上面调用代码相对麻烦,为了简化,装饰器给我们提供了简写方式,如下

  • 装饰器的简写方式: @ + 装饰器函数名
@outer #这里等价于 demo = outer(demo)
def demo():
    print('外部不同需求实现的不同的功能')
demo()
#

总结:

  1. 装饰器函数的简写方式: @ + 装饰器函数名 比如 @outer 等价于 demo = outer(demo)
  2. 在使用装饰器的简写方式的时候,原函数必须放在装饰器函数的下面.

3、装饰器场景练习

下面我们假设一个场景,传递参数 ,计算加、减、

def comput(fn):
    def innner(*args):
        print('这里是公共区域,首先输出需要参与计算的数字',args)
        fn(*args)
    return innner

@comput
def add(a,b):
    print(a+b)

add(3,4)#这里是公共区域,首先输出需要参与计算的数字 (3, 4) 
        #7

@comput
def minus(a,b):
    print(a-b)

minus(3,4)#这里是公共区域,首先输出需要参与计算的数字 (3, 4) 
        #-1

4、多个装饰器修饰同一个函数

# 第一个装饰器:
def outer(fn):
    def inner():
        print("~~~~~~~~111111")
        fn()
    return inner

# 第二个装饰器
def outer1(fn):
    def inner1():
        print("+++++++222222")
        fn()
    return inner1

# 原函数
@outer
@outer1

def demo():
    print("大家可以提前准备购买返程车票了.....")

demo()
'''
运行结果
~~~~~~~~111111
+++++++222222
大家可以提前准备购买返程车票了.....
'''

分析:

其实根据前面的讲解我们知道@装饰器名跟原函数demo我们可以看作一个整体所以 把下面先看做一个整体,

@outer1
def demo():
    print("大家可以提前准备购买返程车票了.....")

通过上面代码执行后demo变成下面这样

def demo():
    print("+++++++222222")
    print("大家可以提前准备购买返程车票了.....")

然后在结合前面的@outer装饰器demo变成下面这样

@outer
def demo():
    print("+++++++222222")
    print("大家可以提前准备购买返程车票了.....")

进一步分解demo变成下面

def demo():
    print("~~~~~~~~111111")
    print("+++++++222222")
    print("大家可以提前准备购买返程车票了.....")

所以最终执行demo()的时候输出结果就是

'''
运行结果
~~~~~~~~111111
+++++++222222
大家可以提前准备购买返程车票了.....
'''

5、带有返回值的装饰器

def outer(fn):
    def inner():
        print("你的爱好是什么?")   # 新增的功能
        return fn()    # 返回  调用原函数的结果
    return  inner # 返回内部函数

# 在原函数的基础上,新增一个功能, 输出: 你的爱好是什么?
# 原函数
@outer
def swim():
    return 'i like swimming.....'

print(swim())
#输出
'''
你的爱好是什么?
i like swimming.....
'''

6、global和nonlocal的使用

全局变量与局部变量

#
num = 11  # 全局变量

def fn():
    num = 98
    print(num)

# fn()   # 98

# print(num)  # 11
  • 当执行fn()时候,使用的是函数内部的变量98,所以结果是 98
  • 但是当直接打印print(num)的时候,函数内部的num是局部变量,外面的num全局变量值是11,这里只能访问到全局变量结果是 # 11

global

  • global关键字的作用是声明当前位置的变量值是取全局变量的值
cc = 123
def demo():
    global cc  # 表示将函数内部的变量声明成了全局变量
    cc = 987
    print(cc)

# demo()   # 987
# print(cc)  # 987

nonlocal

  • nonlocal主要用于闭包函数中
  • nonlocal 目的是声明当前位置的变量取外部函数的变量值
x = 15   # 全局变量
def outer():
    x = 87
    def inner():
        # global x
        # nonlocal x
        x += 1
        print('inner:',x)
    return inner

func1 = outer()
# 在闭包函数中使用global
# func1()   # inner: 16
# func1()   # inner: 17


# 在闭包函数中使用nonlocal  用于外部函数的变量
# func1()    # inner: 88
# func1()    # inner: 89
# func1()    # inner: 90
  1. 上述代码当内部函数位置变量x不用global或者nonlocal修饰时报错
  2. 用global修饰时,取的是全局变量的值 15
  3. 用nonlocal修饰时,取的是外部函数的变量值 87
  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

晨枫阳

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

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

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

打赏作者

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

抵扣说明:

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

余额充值