流畅的Python(七)-函数装饰器和闭包

一、核心要义

主要解释函数装饰器的工作原理,包括最简单的注册装饰器和较复杂的参数化装饰器。同时,因为装饰器的实现依赖于闭包,因此会首先介绍闭包存在的原因和工作原理。

二、代码示例

1、变量作用域规则

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/3 11:26
# @Author  : Maple
# @File    : 01-变量作用域规则.py
# @Software: PyCharm

b = 10
def f1(a):
    print(a)
    print(b)
    # b = 20 # 在函数体中给b赋值,因此会被判断为局部变量

def f1_revise(a):
    global b # 声明函数体中的b为全局变量
    print(a)
    print(b)
    b = 20

"""列表的作用域"""
students = []

def f2():
    # students指向全局变量
    students.append('a')
    return id(students)

def f3():
    # 内部再声明一个 students,其为局部变量,与外部的students不是同一个对象
    students = []
    students.append('a')
    return id(students)

def f4():
    # 声明全局变量
    global students
    students +=[1]
    # print(id(students))
    return id(students)


if __name__ == '__main__':
    # 1. f1测试
    # 说明: 1.Python在编译函数f1的定义体时,判断b为局部变量,因为在函数体中给b赋值了
    #       2.所以在调用函数f1(10)的时候,执行到print(b),发现局部变量b还没有赋值,此时就会报错
    #f1(10) # UnboundLocalError: local variable 'b' referenced before assignment

    # 2. f1_revise测试
    f1_revise(20) # 20,10
    # 全局变量b的值被修改
    print(b) # 20

    # 3.f2测试
    print(f2() == (id(students))) #True

    # 4.f3测试
    print(f3() == (id(students))) #False

    # 5.f4测试
    print(f4() == (id(students))) #True

2、闭包

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/3 11:35
# @Author  : Maple
# @File    : 02-闭包.py
# @Software: PyCharm

"""需求:计算移动平局值
       每调用一次函数,传入一个新的数值,然后和前面的所有值进行累加,再计算最后的平均值
"""

# 1. 借用数组方式实现
def make_avg():
    num_list = []
    def avg(new_value):
        num_list.append(new_value)
        total = sum(num_list)
        return total / len(num_list)
    return avg

# 2.直接使用变量方式实现:但存在一个坑
def make_avg_revise1():
    count =  0
    total = 0
    def avg(new_value):
        count += 1
        total += new_value
        return  total / count
    return avg

# 3.直接使用变量方式实现:填坑
def make_avg_revise2():
    count =  0
    total = 0
    def avg(new_value):
        nonlocal count,total # 通过nonlocal 将变量标记为`自由变量`
        count += 1
        total += new_value
        return total / count
    return avg


if __name__ == '__main__':

    print("***1. make_avg 测试**********************")
    # 1. make_avg 测试
    avg = make_avg()
    # 分析:1.按理说调用完 make_avg_revise1()返回avg1后,make_avg_revise1函数中的局部变量num_list的作用域应该消失了
    #       2.但实际上,在avg1中仍然能够调用num_list,这就是所谓闭包现象(变量的作用域外延了)
    #       3.num_list被称作`自由变量`
    print(avg(5))  # 5.0
    print(avg(10))  # 7.5
    # 查看avg1 创建和绑定的变量
    ## 1-1 创建的局部变量
    print(avg.__code__.co_varnames)  # ('new_value', 'total')
    ## 1-2 绑定的自由变量
    print(avg.__code__.co_freevars)  # ('num_list',)
    ## 自由变量num_list绑定在avg1的closure属性中:是一个cell对象
    print(avg.__closure__)  # (<cell at 0x000001C6095CA760: list object at 0x000001C6095C0900>,)
    ## num_list的值则在cell对象中的cell_contents属性中
    print(avg.__closure__[0].cell_contents)  # [5, 10]

    print("*** 2.调用make_avg_revise1会报错**********************")
    # 2.调用make_avg_revise1会报错
    # 分析:1. 内层函数avg的变量count 和 total在函数内部赋值,因为在函数体编译的时候,会被当作局部变量,但是又没有初始化声明,所以
    #           当函数函数调用的时候,会报错
    try:
        avg = make_avg_revise1()
        print(avg(5))
    except Exception as e:
        print(e)  #local variable 'count' referenced before assignment

    print("***  3.make_avg_revise2测试**********************")
    # 3.make_avg_re
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值