Python 闭包与装饰器

闭包:

1. 闭包是嵌套在函数中的函数。

2. 闭包必须是内层函数对外层函数的变量(非全局变量)的引用。

例如:

上面被红色方框框起来的区域就是闭包,变量a是wrapper()函数的局部变量,它应该是随着wrapper()函数的执行结束之后而消失。但是他没有,是因为此区域形成了闭包,a变量就变成了一个叫自由变量的东西,inner函数的作用域会延伸到包含自由变量a的绑定。也就是说,每次我调用ret对应的inner函数 时,都可以引用到这个自用变量a,这个就是闭包。

我们可以通过一些方法来查看自由变量:

# 函数名.__code__.co_freevars 查看函数的自由变量
# 函数名.__code__.co_varnames 查看函数的局部变量
# 函数名.__closure__ 获取具体的自由变量对象,也就是cell对象。
# cell_contents 自由变量具体的值

闭包的作用:保存局部信息不被销毁,保证数据的安全性。

 

装饰器的目的是什么 ?简单点讲就是达到“开放封闭”,即对扩展是开放的,对源码是封闭。

看下面这个例子:

import time

def index():
    time.sleep(2)  # 模拟一下网络延迟以及代码的效率
    print('欢迎访问博客园主页')
    # return '访问成功'


def timer(func):  # func = index
    def inner():
        start_time = time.time()
        func()
        end_time = time.time()
        print(f'此函数的执行时间为{end_time - start_time}')

    return inner


index = timer(index)
print(index())
#输出:
欢迎访问博客园主页
此函数的执行时间为2.0024185180664062
None

我们先分析一下执行流程:

看清执行流程后我们就能明白为什么最后print打印 的是None, 因为inner函数没有返回值,所以默认就是None.

如果要改变这一情况,那么我们就需要在inner内部将fun()的返回值返回,而且index函数也得提供返回值,而不是默认的None.

加上传参数的功能后:

def home(name, age):
    time.sleep(3)  # 模拟一下网络延迟以及代码的效率
    print(name, age)
    print(f'欢迎访问{name}主页')

def timer(func):  # func = home
    def inner(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        end_time = time.time()
        print(f'此函数的执行时间为{end_time - start_time}')

    return inner

# @timer
home = timer(home)
home('andy', 18)
#输出:
andy 18
欢迎访问andy主页
此函数的执行效率为3.0016109943389893

模板:

from functools import wraps

def wrapper(func):
    @wraps(func)
    def inner(*args,**kwargs):
        '''执行被装饰函数之前的操作'''
        ret = func
        '''执行被装饰函数之后的操作'''
        return ret
    return inner

带参数的装饰器:

import time

from functools import wraps


def outer(arg):
    def timer(func):  # func = home
        @wraps(func)
        def inner(*args, **kwargs):
            if arg < 18:
                print('未成年')
            else:
                print(f'{arg} 已经成年了')
                start_time = time.time()
                res = func(*args, **kwargs)
                end_time = time.time()
                print(f'此函数的执行时间为{end_time - start_time}')
                return res

        return inner

    return timer


@outer(19)
def home(name, age):
    time.sleep(3)  # 模拟一下网络延迟以及代码的效率
    print(name, age)
    print(f'欢迎访问{name}主页')

home('andy', 19)

@outer(19) :分两步:

    第一步先执行outer(19)函数,得到返回值timer

    第二步@与timer结合,形成装饰器@timer 然后在依次执行。

这里通过装饰器传递了一个额外的参数,而因为有这个参数我们就可以做更多的判断,或者其它操作,即扩展更多的功能。

 

上一篇:Python 协程

下一篇:网络协议