闭包和装饰器,两者关系以及装饰器工厂的使用

一、闭包

1. 闭包的概念

  • 用函数的概念说明定义函数的本质

    def test1():
        print("--- in test1 func----")
    
    # 调用函数
    test1()
    
    # 引用函数
    ret = test1
    
    print(id(ret))
    print(id(test1))
    
    #通过引用调用函数
    ret()
    
    • 打印结果
    --- in test1 func----
    140212571149040
    140212571149040
    --- in test1 func----
    
    • 由此可以看出两个函数的内存地址都是一致的,整个过程可以看做:
      • 首先定义一个函数
      • 将标识符test1指向这个函数的内存地址
      • 通过ret = test1,将ret也指向这个函数的地址

2. 闭包

# 定义一个函数
def test(number):

    # 在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包
    def test_in(number_in):
        print("in test_in 函数, number_in is %d" % number_in)
        return number+number_in
    # 其实这里返回的就是闭包的结果
    return test_in


# 给test函数赋值,这个20就是给参数number
ret = test(20)

# 注意这里的100其实给参数number_in
print(ret(100))

#注 意这里的200其实给参数number_in
print(ret(200))

3. 什么是闭包?

  • 函数名只是函数代码空间的引用,当函数名赋值给一个对象的时候 就是引用传递
  • 闭包就是一个嵌套定义的函数,在外层运行时才开始内层函数的定义,然后将内部函数的引用传递函数外的对象
  • 内部函数和使用的外部函数提供的变量构成的整体称为闭包

二、装饰器

1. 有这样的一种形象的比喻装饰器是什么?

  • 内裤是用来遮羞的,但是到了冬天它的功能可能已经无法用来 遮挡风寒,所以人们就发明了长裤,秋裤等各种,保暖满足我的需求,装饰器所谓在内裤外面的长裤,没有改变内裤本身的功能,又给我增添了保暖的作用

  • 装饰器的本质就是一个python的函数,它在不需要更改任何代码的前提下增加额外的功能,

  • 应用场景:

    • 引入日志
    • 函数执行时间统计
    • 执行函数前预备处理
    • 执行函数后清理功能
    • 权限校验等场景
    • 缓存

2. 装饰器的两种实现方式

  • 第一种使用闭包的特点
# 定义函数:完成包裹数据
def makeBold(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrapped

# 定义函数:完成包裹数据
def makeItalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

''' 
@makeBold等价于test1 = makeBold(test1)  
1. 将整个test1 = makeBlod(test1)从右到左执行  
2. 先将test1赋值给makeBlod中的参数fn,test1和fn两个对象指向同一个函数  
3. 由makeBlod返回函数生成一个wrapped函数  
4. 将wrapped赋值给等号左侧的test1,更改了test1的指向  
5. 当实际在下面调用test1函数时,调用的是wrapped函数  
6. wrapped中返回值中的fn()会向外部函数makeBold搜索参数fn  
7. 因此而达到不更改原代码的,在原有基础上增加功能
'''
@makeBold
def test1():
    return "hello world-1"
print(test1())


@makeItalic # 等价于test2 = makeBold(test2)
def test2():
    return "hello world-2"
print(test2())

打印结果

<b>hello world-1</b>
<i>hello world-2</i>
  • 多个装饰器装饰一个函数
# 在上面原有代码的基础上
"""
1. 两个装饰器装饰同一个代码遵循原则,离函数近的装饰器先进行装饰  
2. 下面的装饰器先装饰完后将整个装饰结果给第一个装饰器进行装饰  
3. 可以看做test3 = makeBlod(makeItalic(test3)) 执行过程同上
"""
@makeBold
@makeItalic
def test3():
    return "hello world-3"

打印结果

<b><i>hello world-3</i></b>
  • 装饰器工厂
    使用场景: 装饰器带参数,在原有装饰器的基础上,设置外部变量
import time


def get_run_time(flag):
    """如果flag值为1 print输出的结果为整数;其他则输出浮点数类型
    装饰器工厂函数的功能  
        1. 接收额外参数-->内部代码使用  
        2. 产生装饰器对象
    """
    def get_time(*args, **kwargs):
        #         打包参数 args = (99,) kwargs={}
        func = args[0]
        def inner(*args, **kwargs):
            begin = time.time()
            #   解包参数  (99)   暂存返回值 在最后返回
            ret = func(*args, **kwargs)
            end = time.time()
            if flag == 1:
                print("函数执行花费%d秒" % int(end-begin))
            else:
                print("函数执行花费%f秒" % (end-begin))
            return ret
        return inner
    return get_time

# 1 调用装饰器工厂函数 接收参数生成装饰器 get_time = get_run_time(1)
# 2 @装饰器 对下面的函数进行装饰       func1 = get_time(func1)
@get_run_time(1)
def func1(number):
    time.sleep(0.5)
    print("in func1 这个人有%d岁" % number)
    return 200
    
ret = func1(99)
print(ret)

打印结果

in func1 这个人有99岁
函数执行花费0秒
200

函数内带有参数的情况可以使用不定长参数或者关键字参数进行传参

  • 第二种类装饰器
class mc(object):
    def __init__(self, func):
        """类似装饰器函数的外层函数 接收被装饰的函数引用 保存"""
        self.func = func

    def __call__(self, *args, **kwargs):
        """使用__call__方法让对象 像函数一样()进行调用"""
        print("被装饰的函数开始执行")
        ret = self.func(*args, **kwargs)
        print("执行结束")
        return ret

# 类装饰器只能实现一部分装饰器函数的功能 比如 装饰器工厂

@mc
def f1(number, number2):
    print("in f1")

# f1 = mc(f1) # 实例对象 = 类名()
f1(1,1)  # 实例对象() ----> 实例对象.__call__()

 

原文地址:https://www.cnblogs.com/yanguhung/p/10145768.html