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闭包的使用

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

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关键字完成,具体举例如下

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

二、装饰器

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

装饰器功能特点

  • 不修改已有函数的源代码
  • 不修改已有函数的调用方式
  • 为已有函数增加额外的调用功能
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装饰器使用

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

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.装饰器带有参数的函数

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.装饰器带返回值的函数

# 装饰器带返回值的函数
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.装饰带不定长参数的函数

# 装饰带不定长参数和返回值的函数
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.通用装饰器

# 通用装饰器
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多个装饰器的使用

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

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

" + func() + "

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

Hello World!

单步调试执行过程如下

python闭包与装饰器_第1张图片

2.4带参数的装饰器

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

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函数的装饰,可以添加额外的功能
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/

你可能感兴趣的:(python,python,开发语言,闭包,装饰器,装饰器模式)