>>> from env_helper import info; info()
页面更新时间: 2024-04-04 21:03:05
运行环境:
    Linux发行版本: Debian GNU/Linux 12 (bookworm)
    操作系统内核: Linux-6.1.0-18-amd64-x86_64-with-glibc2.36
    Python版本: 3.11.2

13.7. 闭包和装饰器

本节跟 Python的高级特性3, 神奇的 __call__ 与返回函数密切相关。

13.7.1. 闭包

闭包不好解释,只能先看下面这个例子:

>>> def outer(part1):
>>>        def inner(part2):
>>>                return part2*part1
>>>        return inner
>>>
>>> f = outer(33)
>>> f(100)
3300
>>> f(11)
363

在这个例子中实现了一个功能: f(x) =33*x ,其中f(x)代表了内层函数的功能体,x代表内层函数的参数,也就是说outer函数返回了一个函数,而这个函数的参数(33)来自外层函数,我们可以把外层函数的参数理解成配置,于是通过配置的不同,我们便得到了不同的运算结果,这就叫闭包。

闭包的实现得益于__call__方法,而装饰器就是闭包,也就是说如果要理解装饰器,一定要重视__call__这个内建方法。

13.7.2. 装饰器

装饰器是面向切面编程的基础,Java的AOP相当成熟,它可以很方便的把日志,事务处理等功能给分离出去,而开发者只要专注于业务的开发即可。 Python的AOP编程没有什么认可度很高的轮子,但自己写起来也并不麻烦,下面举一个例子。

假设需要提供一个功能给别人,当别人输入了文件路径,我们这个功能就能把这个文件给读取出来,并给出它的运行时间。

想象一下人格分裂的场景,现在我们需要两个不同的人:装饰器函数和”配置函数”.

装饰器函数:我需要你提供一个文件路径,只要你在函数上加上语法糖,就能知道这个文件的内容和读取时间啦!

配置函数:我返回一个路径就能读取文件了?我书读得少,你可不要骗我。

>>> import time
>>> #装饰器函数
>>> def readFile(func):
>>>     def _deco(path):
>>>         start_time = time.time()
>>>         f = open(func(path),'r')
>>>         end_time = time.time()
>>>         print("readFile costs %ss." % (end_time - start_time))
>>>         return f.read()
>>>     return _deco
>>>
>>> #配置函数
>>> @readFile
>>> def location(path):
>>>     return path
>>>
>>>
>>> print(location('test.txt'))
readFile costs 0.0001251697540283203s.
测试文本信息

这是一种比较简单的装饰器,其实调用是@readFile = readFile(location).

下面再看装饰器带参数的例子,这也是flask里用得最多的。

>>> class locker:
>>>     def __init__(self):
>>>         print("locker.__init__() should be not called.")
>>>
>>>     @staticmethod
>>>     def acquire():
>>>         print("locker.acquire() called.(这是静态方法)")
>>>
>>>
>>> def deco(cls):
>>>     def _deco(func):
>>>         def __deco():
>>>             print("before %s called [%s]." % (func.__name__, cls))
>>>             cls.acquire()
>>>             return func()
>>>         return __deco
>>>     return _deco
>>>
>>> @deco(locker)
>>> def myfunc():
>>>     print(" myfunc() called.")
>>>
>>> myfunc()
before myfunc called [<class '__main__.locker'>].
locker.acquire() called.(这是静态方法)
 myfunc() called.

这段代码参考了:http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html 的第7步.

装饰器写起来比理解起来容易。只有自己着手去写,才能真正理解装饰器。

这里有一篇关于装饰器讲解质量相当高的博文:http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html