资讯专栏INFORMATION COLUMN

Python中的函数装饰器和闭包

caozhijian / 632人阅读

摘要:变量查找规则在中一个变量的查找顺序是局部环境,闭包,全局,内建闭包引用了自由变量的函数。闭包的作用闭包的最大特点是可以将父函数的变量与内部函数绑定,并返回绑定变量后的函数,此时即便生成闭包的环境父函数已经释放,闭包仍然存在。

导语:本文章记录了本人在学习Python基础之函数篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流。

本文重点:

1、掌握装饰器的本质、功能和特点;
2、了解闭包的概念以及Python变量调用规则;
3、了解并学会使用标准库中重要的装饰器;
4、掌握参数化装饰器的意义和代码实现方式。
一、装饰器基础知识

装饰器功能(decorator):将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数。
装饰器本质:是一个返回函数的高阶函数。
装饰器特点:

1、多数装饰器会把被装饰的函数替换成其他函数
2、函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时执行。

装饰器有时采用嵌套函数表示的原因(个人理解):
一些装饰器的装饰功能只有在被装饰函数被调用时方可触发,因此需要用嵌套函数的形式来编写。

二、自由变量、闭包与nonlocal声明

自由变量(free variable):指未在本地作用域绑定的变量,介于局部变量和全局变量之间。
变量查找规则:在python中, 一个变量的查找顺序是 LEGB (L:Local 局部环境,E:Enclosing 闭包,G:Global 全局,B:Built-in 内建).

闭包:引用了自由变量的函数。

闭包的作用:

闭包的最大特点是可以将父函数的变量与内部函数绑定,并返回绑定变量后的函数,此时即便生成闭包的环境(父函数)已经释放,闭包仍然存在。

这个过程很像类(父函数)生成实例(闭包),不同的是父函数只在调用时执行,执行完毕后其环境就会释放,而类则在文件执行时创建,一般程序执行完毕后作用域才释放。

因此对一些需要重用的功能且不足以定义为类的行为,使用闭包会比使用类占用更少的资源,且更轻巧灵活。

nonlocal声明:可以将局部变量声明为自由变量。

eg:计算移动平均值的高阶函数:

def averager():
    sum=0
    n=0
    def avg(i):
        nonlocal sum,n
        sum+=i
        n+=1
        return print(sum/n)
    return avg  
a=averager()
a(3)
a(5)
a(7)

输出分别是3,4,5

三、装饰器进阶使用 1.标准库中的装饰器

Python内置了三个用于装饰方法的函数:property,classmethod和staticmethod
三个重要的内置装饰器:

functools.wraps:
(1)协助构建行为良好的装饰器。
(2)可以把被装饰对象的相关属性复制到装饰器中,默认有 __module__、__name__、__doc__。
(3)个人理解,装饰器在实现装饰的过程中意见把被装饰函数替换了。此时你想调用被装饰函数的__doc__和__name__会发现是none,此时通过functools.wraps就可以在装饰时把相关属性复制过来使用,避免这个问题发生。

functools.lru_cache
(1)实现备忘功能,即缓存,避免发生重复调用来提高效率。
(2)注意调用时必须带括号,因为此装饰器包含maxsize和typed两个参数,带括号表示使用默认参数。否则 functools.lru_cache不清楚该如何执行。

functools.singledispatch
为函数提供重载功能。被其装饰的函数会成为泛函数(generic function):根据第一个参数的类型,用不同的方式执行相同操作的一组函数。

以functools.lru_cache为例实现斐波那契函数的计时装饰器:.

import time
import functools
def clock(func):
    @functools.lru_cache()#减少重复自引用,避免重复计算
    def clocked(arg):
        t0=time.perf_counter()
        func(arg)
        result=func(arg)
        t1=time.perf_counter()
        processtime=t1-t0
        name=func.__name__
        print("[{0:.8f}] {1}({2})={3}".format(processtime,name,arg,result))
        return result#非常重要,否则破坏原fibs函数导致无法递归调用。
    return clocked
 
@clock
def fibs(n):
    if n<2:
        return 1
    else:
        return fibs(n-1)+fibs(n-2)
fibs(6)#输出8
2.叠放装饰器

如同函数可以嵌套使用,装饰器亦可以叠放起来装饰同一对象。语法如下:
@d1
@d2
def f1():
pass
上述装饰操作等价于d1(d2(f)),故易知叠放在上端的装饰器靠后执行。

3.参数化装饰器

普通装饰器的进阶版,通过参数接口可以更方便的定制我们需要的装饰器,适应需求变化。
代码实现较普通装饰器多嵌套一层函数,用来传递用户输入的参数。以计时器举例形式如下:

import time
DEFAULT_FMT = "[{elapsed:0.8f}s] {name}({args}) -> {result}"
def clock(fmt=DEFAULT_FMT): #三层函数来实现
    def decorate(func): 
        def clocked(*_args): 
            t0 = time.time()
            _result = func(*_args) 
            elapsed = time.time() - t0
            name = func.__name__
            args = ", ".join(repr(arg) for arg in _args) 
            result = repr(_result) 
            print(fmt.format(**locals())) 
            return _result 
        return clocked 
    return decorate 
if __name__ == "__main__":
    @clock()
    def snooze(seconds):
        time.sleep(seconds)
    for i in range(3):
        snooze(.123)

输出:

[0.12480044s] snooze(0.123) -> None
[0.13660240s] snooze(0.123) -> None
[0.12480044s] snooze(0.123) -> None
4.类装饰器

上边实现参数化装饰器的代码由于包含三重嵌套略显复杂, 事实上复杂的装饰器用 class 实现更方便。通过 __call__ 方法即可改写参数化装饰器。具体实现留作勤劳的你去课后思考,感兴趣的可以与我私信交流。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/41475.html

相关文章

  • Python学习之路26-函数装饰器和闭包

    摘要:初步认识装饰器函数装饰器用于在源代码中标记函数,以某种方式增强函数的行为。函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时运行。只有涉及嵌套函数时才有闭包问题。如果想保留函数原本的属性,可以使用标准库中的装饰器。 《流畅的Python》笔记本篇将从最简单的装饰器开始,逐渐深入到闭包的概念,然后实现参数化装饰器,最后介绍标准库中常用的装饰器。 1. 初步认识装饰器 函数装饰...

    sunny5541 评论0 收藏0
  • 流畅的python读书笔记-第七章-函数装饰器和闭包

    摘要:函数装饰器和闭包严格来说,装饰器只是语法糖。何时执行装饰器它们在被装饰的函数定义之后立即运行。装饰器突出了被装饰的函数的作用,还便于临时禁用某个促销策略只需把装饰器注释掉。 函数装饰器和闭包 严格来说,装饰器只是语法糖。如前所示,装饰器可以像常规的可调用对象那样调用,其参数是另一个函数。有时,这样做更方便,尤其是做元编程(在运行时改变程序的行为)时。 Python何时执行装饰器 它们在...

    Hydrogen 评论0 收藏0
  • Python_装饰器和生成器

    摘要:迭代器迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象,迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束,迭代器只往前不会往后退。生成器特点保存了一套生成数值的算法。 迭代器 迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象,迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束,迭代器只往前不会往后退。 可迭代对象 以直接...

    sugarmo 评论0 收藏0
  • Python 函数式编程、装饰器以及一些相关概念简介

    摘要:重写內建名字空间中的函数闭包闭包是词法闭包的简称。另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。 Python 中的 Decorator(装饰器) 是对一个函数或者方法的封装,从而使其可以完成一些与自身功能无关的工作。 预备知识 一切皆对象 在 Python 中,所有的一切都被视为对象,任何的变量、函数、类等都是 object 的子类。因此除了变量之外,函数和类等也可以...

    Jinkey 评论0 收藏0

发表评论

0条评论

caozhijian

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<