python闭包与装饰器

news/2024/12/30 22:14:22 标签: python, 开发语言, 闭包, 装饰器, 装饰器模式

一、闭包

闭包定义:在函数嵌套(函数内再定义函数)的前提下,内部函数使用了外部函数变量或参数,且外部函数返回了内部函数,则将这个使用了外部函数变量的内部函数称为闭包

闭包作用特点

  • 可以保存外部函数内的变量,不会随外部函数调用完而销毁
  • 闭包可以提高代码的可重用性,无需再手动定义额外的功能函数
  • 由于闭包引用了外部函数变量,外部函数变量没有及时释放,消耗内存
python">def outFunc(a):  # 定义外部函数,a=5
    b = 2
    def inFunc(c):  # 定义内部函数 c=1和3
        result = a + b + c   # 内部函数使用了外部函数参数a和变量b
        print('结果为:', result)
    return inFunc  # 外部函数返回内部函数,该内部函数即为闭包,是一个引用,该引用存入了out对象中,故执行out()时相当于运行了inFunc()函数
out = outFunc(5)  # 创建闭包实例,执行outFunc函数,传参给了a
out(1)  # 执行闭包inFunc函数,传参给了c
out(3)


输出:   # 由结果可知闭包保存了外部函数的参数a和变量b,每次执行闭包都是在a=5,b=2基础上计算
结果为: 8
结果为: 10

1.1闭包的使用

根据配置使用闭包实现多人之间的对话

python">def configName(name):
    def sayInfo(info):
        print(name + ": " + info)
    return sayInfo

xy = configName("XY")
xy("你好,在不")
xy("你好!在不在?")

yx = configName("YX")
yx("不在。")

输出:
XY: 你好,在不
XY: 你好!在不在?
YX: 不在。!

1.2修改闭包内使用的外部变量

使用nonlocal关键字完成,具体举例如下

python">def outFunc(a):  # a=1
    b = 2
    def inFunc(c):  # c=8和3
        # a = 5 # 错误修改,本意要修改外部参数a的值,实际在内部函数定义了一个局部变量a

        # 正确修改
        nonlocal a   # 告诉解释器,此处使用的是外部变量a
        a = 5   # 修改
        result = a + b + c
        print('结果为:', result)
    print('修改前的a值:', a)   # a=1
    inFunc(8)
    print('修改后的a值:', a)   # a=5
    return inFunc   # 外部函数返回内部函数,该内部函数即闭包
out = outFunc(1)  # 创建闭包实例,执行outFunc函数,a=1
out(3)  # 执行闭包函数inFunc,c=3

输出:
修改前的a值: 1
结果为: 15
修改后的a值: 5
结果为: 10

二、装饰器

装饰器定义:指为已有函数增加额外功能的函数,本质上就是一个闭包函数,闭包函数有且只有一个参数并为函数类型,规定已实现的功能代码不允许被修改,但可以被扩展

装饰器功能特点

  • 不修改已有函数的源代码
  • 不修改已有函数的调用方式
  • 为已有函数增加额外的调用功能
python">def outFunc(out):   # 定义装饰器,out为被装饰的目标函数,只有一个参数,且为函数类型(下面调用了)
    def inFunc():   # 在内部函数中对已有函数进行装饰
        print('额外功能……')
        out()   # 执行被装饰的目标函数,即执行调用ordinaryFunc函数
    return inFunc

# def ordinaryFunc():
#     print('已有普通函数……')
# ordinaryFunc = outFunc(ordinaryFunc)   # 调用装饰器来装饰已有的ordinaryFunc函数,ordinaryFunc=inFunc
# ordinaryFunc()  # 执行inFunc函数

# 语法糖写法,书写格式为:@装饰器名字,通过语法糖可以完成对已有函数的装饰
@outFunc   # 等价于ordinary = outFunc(ordinaryFunc)
def ordinaryFunc():  # 已有函数不变
    print('已有普通函数……')
ordinaryFunc()   # 执行inFunc函数,调用方式不变

输出:
额外功能……
已有普通函数……

2.1装饰器使用

实例:装饰器实现统计已有函数执行时间,可知装饰器能够在不改变已有函数源代码及调用方式的前提下,对已有函数进行功能的扩展

python">import time
def getTime(func):
    def inFunc():
        begin = time.time()
        func()
        end = time.time()
        print('func()函数的执行时间为:%.5f' % (end-begin))
    return inFunc
@getTime
def ordinaryFunc():
    for i in range(100000):
        print('这是第%d条数据!' % i)
ordinaryFunc()

输出:
……
……
这是第99998条数据!
这是第99999条数据!
func()函数执行时间为:1.77821

2.2通用装饰器的使用

1.装饰器带有参数的函数

python">def outFunc(func):
    def inFunc(a, b):  # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
        func(a, b)
    return inFunc
@outFunc  # addSum = outFunc(addSum),即addSum=inFunc
def addSum(c, d):
    sum = c + d
    print('结果为:', sum)
addSum(10, 3)  # 调用执行inFunc

输出:
结果为: 13

2.装饰器带返回值的函数

python"># 装饰器带返回值的函数
def outFunc(func):
    def inFunc(a, b):
        result = func(a, b)  # 调用执行sum()
        return result
    return inFunc
@outFunc
def sum(c, d):
    sum = c + d
    return sum

sum2 = sum(10, 3)  # 调用执行inFunc
print('结果为:', sum2)

输出:
结果为: 13

3.装饰带不定长参数的函数

python"># 装饰带不定长参数和返回值的函数
def outFunc(func):
    def inFunc(*args, **kwargs):  # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
        func(*args, **kwargs)  # 执行addSum
    return inFunc
@outFunc  # addSum = outFunc(addSum),即addSum=inFunc
def addSum(*args, **kwargs):
    sum = 0
    for v in args:
        sum += v
    for v in kwargs.values():
        sum += v
    print('结果为:', sum)
addSum(1, 3, a=5, b=2, c=2)  # 调用执行inFunc

输出:
结果为: 13

4.通用装饰器

python"># 通用装饰器
def outFunc(func):
    def inFunc(*args, **kwargs):  # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
        result = func(*args, **kwargs)  # 执行addSum
        return result
    return inFunc
@outFunc  # addSum = outFunc(addSum),即addSum=inFunc
def addSum(*args, **kwargs):
    sum = 0
    for v in args:
        sum += v
    for v in kwargs.values():
        sum += v
    return sum

@outFunc
def subtraction(a,b):
    result1 = a - b
    print('a - b结果为:', result1)
result2 = addSum(1, 3, a=5)  # 调用执行inFunc,1、3为args元组,a=5为kwargs字典
print('结果为:', result2)
subtraction(9, 4)   # 调用执行inFunc,9、4为args元组

输出:
结果为: 9
a - b结果为: 5

2.3多个装饰器的使用

装饰过程:离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,即由内到外的装饰

python">def outFunc(func):
    def inFunc(*args, **kwargs):  # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
        return "<div>" + func() + "</div>"
    return inFunc
def outFunc2(func):
    def inFunc2(*args, **kwargs):  # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
        return "<p>" + func() + "</p>"
    return inFunc2

@outFunc
@outFunc2
def info():
    return "Hello World!"
result = info()  # 首先执行inFunc
print('结果为:', result)

输出:
结果为: <div><p>Hello World!</p></div>

单步调试执行过程如下

2.4带参数的装饰器

使用带参数的装饰器,需在装饰器外再包裹一个函数,该函数用来接收参数,返回是装饰器,因为 @符号需要配合装饰器实例使用

python">def out(flag):   # 在装饰器外包裹一个out函数,用来接收参数,返回装饰器,因为@后必须为装饰器实例
    def outFunc(func):  # 装饰器只能接收一个参数,且只能是函数类型,故需在外部再加一个out函数
        def inFunc(c, d):  # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
            if flag=="+":
                print("正在进行加法运算")
            elif flag=="-":
                print('正在进行减法运算')
            result = func(c, d)
            return result
        return inFunc
    return outFunc   # 返回装饰器

@out("+")   # 使用装饰器装饰函数
def add(a, b):
    sum = a + b
    return sum

@out("-")
def sub(a, b):
    result = a - b
    return result

result1 = add(3, 5)  # 执行inFunc,先进行加减判定,再执行func(),根据判定确定执行add()或sub()函数
result2 = sub(5, 2)
print('加法运算result1结果为:%d,减法运算result2结果为:%d' % (result1, result2))

输出:
正在进行加法运算
正在进行减法运算
加法运算result1结果为:8,减法运算result2结果为:3

2.5类装饰器

  • 要想类的实例对象能够像函数一样调用,需在类中使用call方法,把类的实例变成可调用对象(callable),即能像调用函数一样进行调用
  • call方法里进行对func函数的装饰,可以添加额外的功能
python">class decorate(object):   # 类装饰器
    def __init__(self, func):
        self.func = func
    def __call__(self, *args, **kwargs):  # 实现该方法,表示对象是一个可调用对象,能像调用函数一样进行调用
        print('添加装饰功能')
        self.func()   # 执行info()方法

@decorate  # 等价于info = decorate(info),故需提供一个init方法,并多增加一个func参数
def info():
    print('已有普通函数')
info()  # 执行__call__()方法

输出:
添加装饰功能
已有普通函数

 学习导航:http://xqnav.top/


http://www.niftyadmin.cn/n/4803.html

相关文章

热门Java开发工具IDEA入门指南——了解并学习IDE

IntelliJ IDEA&#xff0c;是java编程语言开发的集成环境。IntelliJ在业界被公认为最好的java开发工具&#xff0c;尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能可以说是超常的。 本…

信息安全技术 关键信息基础设施安全保护要求2022版附下载地址

信息安全技术 关键信息基础设施安全保护要求 2022年10月12日&#xff0c;国家市场监督管理总局&#xff08;国家标准化管理委员会&#xff09;批准GB/T 39204-2022《信息安全技术 关键信息基础设施安全保护要求》&#xff08;以下简称“安全保护标准”&#xff09;推荐性国家标…

孩子升年级难适应?猿辅导语文金牌教研来支招

转眼间&#xff0c;开学已经两个月&#xff0c;不知道孩子们对新学年适应得如何了&#xff1f;猿辅导发现&#xff0c;多数孩子在新环境里&#xff0c;一两个月就适应得很好。但也有的孩子适应得比较慢&#xff0c;一学期下来&#xff0c;感觉还不是很好&#xff0c;这就需要尽…

【JavaScript】Math对象知识全解

文章目录前言常用属性常用方法实例参考前言 除了简单的加减乘除&#xff0c;在某些长和开发者需要进行更为复杂的数学运算。JavaScript的Math对象提供了一系列属性和方法&#xff0c;能够满足大多数场合的需求。 Math对象是JavaScript的全局对象&#xff0c;不需要由函数进行创…

9. android 动态音频策略的原理

基于Android 11 aosp 源码分析。 目录 理解 AudioManager.java&#xff0c; 提供了注册外部策略的接口&#xff1a; 注入的mAudioPolicy包含了什么信息&#xff1a; 外部策略如何被系统触发: 音量控制 焦点请求 设备选择 理解 针对与Car&#xff0c; Android 将音…

安装【银河麒麟V10】linux系统

最近客户的服务器是麒麟的操作系统&#xff0c;因为要在上面安装我们的应用所以&#xff0c;要找镜像在本地搭建测试环境&#xff0c;看看有什么问题&#xff0c;把遇到的问题和操作总结一下。因为客户的服务器是内网&#xff0c;没网还需要挂载镜像&#xff0c;所以还需要挂载…

基于Matlab有效跟踪大量目标仿真(附源码)

目录 一、介绍 二、使用默认分配成本计算 三、使用粗略工作分配成本计算 四、使用外部成本计算 五、更改 GNN 分配算法 六、蒙特卡洛模拟 七、总结 八、程序 此示例演示如何使用 thes 跟踪大量目标。类似的技术也可以应用于theandas。 一、介绍 在许多应用中&#xff0c;…

动态规划学习4:5 最长回文子串 三种方法

方法一 暴力算法 class Solution { public:bool if_same(string& str,int left,int right){while (left<right){if (str[left]!str[right]){return false;}left;right--;}return true; }string longestPalindrome(string s) {string res s.substr(0,1);for (int i 0…