【博学谷学习记录】超强总结,用心分享|人工智能第十二课Python-闭包与装饰器 !

一、闭包

1、作用域

在Python代码中,作用域分为两种情况:全局作用域 与 局部作用域

2、变量的作用域

在全局定义的变量 => 全局变量

在局部定义的变量 => 局部变量

3、全局变量与局部变量的访问范围

① 在全局作用域中可以访问全局变量,在局部作用域中可以访问局部变量

# 全局作用域(全局变量)
num1 = 10
def func():
    # 局部作用域(局部变量)
    num2 = 20
    # ① 在局部访问局部变量
    print(num2)
​
# ① 在全局访问全局变量
print(num1)
# 调用函数
func()

② 在局部作用域中可以访问全局变量

# 全局作用域(全局变量)
num1 = 10
def func():
    # 局部作用域(局部变量)
    # ② 在局部作用域中可以访问全局变量
    print(num1)
​
# 调用函数
func()

③ 在全局作用域中不能访问局部变量

# 全局作用域(全局变量)
num1 = 10
def func():
    # 局部作用域(局部变量)
    num2 = 20
​
# 调用函数
func()
# 在全局作用域中调用局部变量num2
print(num2)

运行结果会发生报错。变量名称未被定义。

4、全局作用域中无法访问局部变量原因

在Python的底层存在一个“垃圾回收机制”,主要的作用就是回收内存空间。加快计算机的运行。我们在Python代码中定义的变量也是需要占用内存的,所以Python为了回收已经被已经过的内存,会自动将函数运行以后的内部变量和程序直接回收。

5、闭包可以把函数内部的局部变量保留

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

6、闭包的构成条件(三步走)

第一步:有嵌套

第二步:有引用

第三步:有返回(return)

'''
闭包程序三步走:① 有嵌套 ② 有引用 ③ 有返回
'''
​
def func():
    num = 20  # 局部变量
    def inner():
        print(num)
    return inner  # 实际上inner函数并没有执行,只是返回了inner函数在内存中的地址
​
f = func()  # 相当于把inner在内存中的地址0x7fbc9b3f8e18赋值给变量f
f()  # 找到inner函数的内存地址,并执行器内部的代码(num=20),在于闭包函数保留了num=20这个局部变量

闭包的作用:正常情况下,当执行func()的时候,函数内部的变量num = 20,会随着函数的func函数的结束而被垃圾回收机制所回收。所以闭包的真正作用:就是可以在全局作用域中,实现间接对局部变量进行访问。

7、注意事项

由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存。

8、在闭包的内部实现对外部变量的修改

新知识点:nonlocal关键字(在函数内部修改函数外部的变量,这个变量非全局变量)

老知识点:global关键字(在函数内部声明变量,代表引用全局作用域中的全局变量)

'''
Python闭包:① 有嵌套 ② 有引用 ③ 有返回
'''
​
def outer():
    num = 10
    def inner():
        # 这种写法无法实现通过闭包修改外部的局部变量'
        nonlocal num
        num = 20
    print('outer函数中的num:', num)  # 10
    inner()  # 执行函数inner,让num=20生效
    print('outer函数中的num:', num)  # 20
    return inner
​
f = outer()
f()

二、装饰器

1、什么是装饰器

在不改变现有函数源代码以及函数调用方式的前提下,实现给函数增加额外的功能。

装饰器的本质就是一个闭包函数(三步:① 有嵌套 ② 有引用 ③ 有返回)

有返回代表外部函数返回内部函数的内存地址(内部函数的名称)

2、装饰器的雏形

# 要求:把登录功能封装起来(比如封装成一个函数,添加这个登录不能影响现有功能函数)
'''
装饰器:本质是一个闭包,有嵌套、有引用、有返回(返回的是函数的内存地址)
参数fn在check中也是一个局部变量
参数fn:就是要装饰的函数的函数名,如comment,如download
'''
def check(fn):
    def inner():
        # 开发登录功能
        print('登录功能')
        # 调用原函数
        fn()
    return inner
​
​
# 评论功能(前提:登录)
def comment():
    print('评论功能')
​
comment = check(comment)
comment()
​
# 下载功能(前提:登录)
def download():
    print('下载功能')
​
download = check(download)
download()

3、装饰器定义

'''
装饰器:本质就是一个闭包 ① 有嵌套 ② 有引用 ③ 有返回
'''
def check(fn):
    
    def inner():
        # 开发登录验证功能
        print('验证登录')
        # 执行原有函数
        fn()
    return inner
​
@check
def comment():
    print('发表评论')
​
comment()

4、装饰器的作用:获取程序的执行时间

'''
定义获取程序的执行时间装饰器 => 闭包(① 有嵌套 ② 有引用 ③ 有返回)
'''
import time
​
def get_time(fn):
    def inner():
        # ① 添加装饰器修饰功能(获取程序的执行时间)
        begin = time.time()
        # ② 调用fn函数,执行原函数代码
        fn()
        end = time.time()
        print(f'这个函数的执行时间:{end - begin}')
    return inner
​
​
@get_time
def demo():
    for i in range(1000000):
        print(i)
​
demo()

5、带有参数装饰器

'''
带有参数的装饰器:① 有嵌套 ② 有引用 ③ 有返回
'''
def logging(fn):
    def inner(*args, **kwargs):
        # 添加装饰器代码(输出日志信息)
        print('-- 日志信息:正在努力计算机 --')
        # 执行要修饰的函数
        fn(*args, **kwargs)  # sum_num(a, b)
    return inner
​
@logging
def sum_num(*args, **kwargs):
    result = 0
    # *args代表不定长元组参数,args = (10, 20)
    for i in args:
        result += i
    # **kwargs代表不定长字典参数, kwargs = {a:30, b:40}
    for i in kwargs.values():
        result += i
    print(result)
​
# sum_num带4个参数,而且类型不同,10和20以元组形式传递,a=30,b=40以字典形式传递
sum_num(10, 20, a=30, b=40)

6、带有返回装饰器

'''
带有返回值的装饰器:① 有嵌套 ② 有引用 ③ 有返回
如果一个函数执行完毕后,没有return返回值,则默认返回None
'''
def logging(fn):
    def inner(*args, **kwargs):
        print('-- 日志信息:正在努力计算 --')
        return fn(*args, **kwargs)  # fn() = sub_num(20, 10) = result
    return inner
​
@logging
def sub_num(a, b):
    result = a - b
    return result
​
print(sub_num(20, 10))

7、通用版本的装饰器

'''
通用装饰器:① 有嵌套 ② 有引用 ③ 有返回 ④ 有不定长参数 ⑤ 有return返回值
'''
def logging(fn):
    def inner(*args, **kwargs):
        # 输出装饰器功能
        print('-- 正在努力计算 --')
        # 调用fn函数
        return fn(*args, **kwargs)
    return inner
​
​
@logging
def sum_num1(a, b):
    result = a + b
    return result
​
print(sum_num1(20, 10))
​
@logging
def sum_num2(a, b, c):
    result = a + b + c
    return result
​
print(sum_num2(10, 20, 30))

8、装饰器高级:使用装饰器传递参数

基本语法:

def 装饰器(fn):
    ...
​
@装饰器('参数')
def 函数():
    # 函数代码

9、扩展:类装饰器(了解)

装饰器还有一种特殊的用法就是类装饰器,就是通过定义一个类来装饰函数。

class 类装饰器():
    # 装饰器代码
​
@类装饰器名称
def 函数():
    # 函数代码

类装饰器编写规则:
① 必须有一个__init__初始化方法,用于接收要装饰函数的函数 
② 必须把这个类转换为可以调用的函数

本期的笔记分享就到这里啦,下期分享Python对mysql数据库的操作 ! 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值