Python的程序结构[8] -> 装饰器/Decorator -> 装饰器浅析
装饰器 / Decorator
目录
1 关于闭包 / About Closure
装饰器其本质是一个闭包函数,为此首先理解闭包的含义。
闭包(Closure),又称词法闭包(Lexical Closure)或函数闭包(Function Closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体,这种组合使用一种映射关系将函数的自由变量(在 local 使用但是在 enclosing scope 使用的变量)与函数相关联。
2 装饰器的本质 / Essence of Decorator
装饰器其实本质源于闭包的函数,这个闭包函数将一个函数作为参数传入,然后返回一个替代版的函数。
A decorator is a function that takes a function object as an argument, and returns a function object as a return value.
下面以一段代码为例进行解释
# Enclosure Function and Decorator
def deco(func):
# This is a sbstitute returned to replace input function
def wrapper():
return func()+1
return wrapper def foo():
return 1 print(foo()) #
print(deco(foo)()) #
首先定义一个函数 deco,在 deco 函数内再定义一个 wrapper 函数,此时 deco 函数内部,wrapper 函数外部,就是 wrapper 的闭包范围,wrapper 会记录到环境内的关系,同时根据 LEGB 搜索原则找寻参数。因此在 return 的时候可以到 func 函数。
Note: 此处的 deco 函数的主要作用在于,对传入的 func 做一定的处理后,返回一个新的函数 wrapper 作为替代。
随后定义一个 foo 函数作为传入的参数,通过输出语句可以看到,经过 deco 函数装饰的 foo 在不改动内部函数的前提下,完成了对函数行为的改变。这样一个 deco 函数,就是装饰器的本质起源。
3 语法糖 / Syntactic Sugar
前面介绍的装饰器本质方法依旧存在一个麻烦需要处理,也就是当需要对所有的被装饰函数都进行相同的装饰时,需要在每个调用的位置都调用一次装饰函数,这样无疑增加了代码的修改难度。
为此语法糖@便出现了,利用语法糖可以很方便的装饰一个函数,只需要在被装饰的函数之上使用@ deco便可以将函数 deco 装饰到被装饰的函数 fox上,而当再次调用 fox 时,首先会调用一次 fox = deco(fox),自动完成对函数的装饰。
# Use Syntactic Sugar to make Decorator
@ deco
def fox():
return 1
print(fox()) #
同时,语法糖也是可以叠加使用的,而调用的顺序与声明的顺序是相反的,即自下而上。
@ deco
@ deco
def fox():
return 1
print(fox()) #
4 装饰器传入参数 / Decorator Parameter Pass
装饰器的参数传入主要有两种,一是能够装饰需要传入参数的函数,二是装饰器自身参数。
首先介绍如何使装饰器装饰带参数传入的函数,最直接的方式是定义返回函数 wrapper 时,将其定义成与被装饰函数 func 相同的形参结构,这样返回的 wrapper 函数便能够接受 func 的参数传入。利用装饰器函数和语法糖分别进行验证,可以得到期望的结果。
# Make a Decorator can receive function para
def decor(func):
def wrapper(a, b):
return func(a, b)+1
return wrapper def fun(a, b):
return a+b @ decor
def funx(a, b):
return a+b print(fun(1, 2)) #
print(decor(fun)(1, 2)) #
print(funx(1, 2)) #
但是上面的方法具有局限性,当我们不知道被装饰函数需要传入的参数数量时,便无法定义内部的返回函数 wrapper。同时,当被装饰函数的输入不同时,装饰器的通用性便被破坏。为此可以利用运算符 * 和 ** 来对任意参数进行元组或字典的解包,从而完成一个通用的装饰器。
# Common function para Decorator
def decor(func):
def wrapper(*argv, **kwargv):
return func(*argv, **kwargv)+1
return wrapper @ decor
def fun(a, b, c=0):
return a+b+c @ decor
def funx(a, b, c, d, e=0):
return a+b+c+d+e print(fun(1, 2, c=3)) #
print(funx(1, 2, 3, 4, e=5)) #
同样的,对于装饰器,也可以传入一个参数,可利用这个参数完成诸如装饰器开关等操作(设置 True 和 False 来确定返回 _wrapper 还是原函数 func)。要实现这一功能,则需要在装饰器外再多一层函数,下面的 decora 函数实质上是起到返回装饰器函数 _décor 的作用,语法糖修饰的 @ decora(3) 实际上等价于 @ _decor,因为 decora(3) 返回的是一个函数 _decor。同时,在此处也体现了函数闭包的特性,即在内层函数中记录了闭包环境中的参数 c 值。
# Decorator receive its own para
def decora(c):
def _decor(func):
def _wrapper(a, b):
return func(a, b)+c
return _wrapper
return _decor # Note: decora(3) return _decor
# @ decora(3) equals to @ _decor
# _decor record c value thanks to Closure
@ decora(3) # @ _decor
def fun(a, b):
return a+b print(fun(1, 2)) #
相关阅读
1. LEGB 搜索原则
参考链接
http://blog.csdn.net/slvher/article/details/39227815
Python的程序结构[8] -> 装饰器/Decorator -> 装饰器浅析的更多相关文章
- Python的程序结构[7] -> 生成器/Generator -> 生成器浅析
生成器 / Generator 目录 关于生成器 生成器与迭代器 生成器的建立 通过迭代生成器获取值 生成器的 close 方法 生成器的 send 方法 生成器的 throw 方法 空生成器的检测方 ...
- Python的程序结构[1] -> 方法/Method[1] -> 静态方法、类方法和属性方法
静态方法.类方法和属性方法 在 Python 中有三种常用的方法装饰器,可以使普通的类实例方法变成带有特殊功能的方法,分别是静态方法.类方法和属性方法. 静态方法 / Static Method 在 ...
- Python基本程序结构
条件判断: 计算机之所以能做很多自动化的任务,因为它可以自己做条件判断.比如,输入用户年龄,根据年龄打印不同的内容,在Python程序中,用if语句实现:
- Python的程序结构[1] -> 方法/Method[0] -> 类实例方法、私有方法和抽象方法
类实例方法.私有方法和抽象方法 Python中最常用的就是类实例方法,类似于属性中的类实例属性,同时,也存在与私有属性类似方法,即私有方法,下面介绍这两种常见的方法,以及一种特殊意义的类实例方法 -- ...
- Python的程序结构[1] -> 方法/Method[2] -> 魔术方法 __init__ / __del__ / __new__
魔术方法 / Magic Method 魔法方法就是可以给你的类增加魔力的特殊方法(实质应称为特殊方法,魔术方法在JavaScript中有所体现,对象具有不透明特性,而且无法在自定义对象中模拟这些行为 ...
- Python入门-程序结构扩展
deque双端队列 #双端队列,就是生产消费者模式,依赖collections模块 from collections import deque def main(): info = deque((&q ...
- Python的程序结构[0] -> 属性/Property[0] -> 类属性、实例属性和私有属性
类属性.实例属性和私有属性 Python中类的属性主要包括类属性,实例属性和私有属性,下面是对三种属性的简单介绍 类属性 / Class Property 类属性在__init__()之外初始化,在外 ...
- Python的程序结构[2] -> 类/Class[0] -> 类的特殊属性
类的特殊属性 / Special Property of Class Python 中通过 class 进行类的定义,类可以实例化成实例并利用实例对方法进行调用. 类中还包含的一些共有的特殊属性. 特 ...
- Python的程序结构[2] -> 类/Class[1] -> 基类与继承
基类与继承 / Base Class and Inheritance Class 面向对象的特性使得 Python 中不可避免地需要使用到类和类的继承,类的继承可以使得代码很好的被重用.下面以一些代码 ...
随机推荐
- 【Hazard of Overfitting】林轩田机器学习基石
首先明确了什么是Overfitting 随后,用开车的例子给出了Overfitting的出现原因 出现原因有三个: (1)dvc太高,模型过于复杂(开车开太快) (2)data中噪声太大(路面太颠簸) ...
- iOS笔记055 - UI总结01
1.程序启动后的开始动画 程序启动后可以加载一个简单的动画界面来介绍程序或者用户信息. 可以使用一个xib来描述界面.并且如果想在程序加载完成后第一个加载这个xib文件,需要在Appdelegat ...
- 什么是 IRC?
IRC是Internet Relay Chat 的英文缩写,中文一般称为互联网中继聊天.它是由芬兰人Jarkko Oikarinen于1988年首创的一种网络聊天协议.经过十年的发展,目前世界上有超过 ...
- Python3.7在win10下安装PyAudio库以及实现音频的录制与播放
Python3.7 无法安装pyaudio 度娘的结果基本都是这个,pip install pyaudio.....然而十有八九你的电脑不买账,会报错. 报错信息: running install r ...
- (笔记) RealTimeRender[实时渲染] C2
@author: 白袍小道 @来源:RealTime Render @建议书籍:龙书.RealTimeR第四版.GPUGem和PRO (来源:暗影不解释) 引点 这一章关注的管线中的管道功能,而非实现 ...
- C++ Primer 第2章 变量和基本类型
C++ Primer 第2章 变量和基本类型 C Primer 第2章 变量和基本类型 1 基本内置类型 算数类型 类型转换 字面值常量 2 变量 变量定义 3 复合类型 引用d左引用 指针d 4 c ...
- rownum浅谈(一)
只要做web开发,几乎没有不需要分页查询的,在oracle中,rownum就是用来进行处理分页的. 1.rownum是oracle对结果集返回的一个伪列,也就是说是先查询完结果之后再加上的一个虚列,相 ...
- 团队冲刺Alpha(六)
目录 组员情况 组员1(组长):胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:凯琳 组员6:翟丹丹 组员7:何家伟 组员8:政演 组员9:黄鸿杰 组员10:刘一好 组员11:何宇恒 展示 ...
- jmeter的build.xml
<?xml version="1.0"?><!-- Licensed to the Apache Software Foundation (ASF) unde ...
- [洛谷P4178]Tree
题目大意:给一棵树,问有多少条路径长度小于等于$k$ 题解:点分治 卡点:无 C++ Code: #include <cstdio> #include <algorithm> ...