装饰器 / Decorator


目录

  1. 关于闭包
  2. 装饰器的本质
  3. 语法糖
  4. 装饰器传入参数

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 -> 装饰器浅析的更多相关文章

  1. Python的程序结构[7] -> 生成器/Generator -> 生成器浅析

    生成器 / Generator 目录 关于生成器 生成器与迭代器 生成器的建立 通过迭代生成器获取值 生成器的 close 方法 生成器的 send 方法 生成器的 throw 方法 空生成器的检测方 ...

  2. Python的程序结构[1] -> 方法/Method[1] -> 静态方法、类方法和属性方法

    静态方法.类方法和属性方法 在 Python 中有三种常用的方法装饰器,可以使普通的类实例方法变成带有特殊功能的方法,分别是静态方法.类方法和属性方法. 静态方法 / Static Method 在 ...

  3. Python基本程序结构

    条件判断: 计算机之所以能做很多自动化的任务,因为它可以自己做条件判断.比如,输入用户年龄,根据年龄打印不同的内容,在Python程序中,用if语句实现:

  4. Python的程序结构[1] -> 方法/Method[0] -> 类实例方法、私有方法和抽象方法

    类实例方法.私有方法和抽象方法 Python中最常用的就是类实例方法,类似于属性中的类实例属性,同时,也存在与私有属性类似方法,即私有方法,下面介绍这两种常见的方法,以及一种特殊意义的类实例方法 -- ...

  5. Python的程序结构[1] -> 方法/Method[2] -> 魔术方法 __init__ / __del__ / __new__

    魔术方法 / Magic Method 魔法方法就是可以给你的类增加魔力的特殊方法(实质应称为特殊方法,魔术方法在JavaScript中有所体现,对象具有不透明特性,而且无法在自定义对象中模拟这些行为 ...

  6. Python入门-程序结构扩展

    deque双端队列 #双端队列,就是生产消费者模式,依赖collections模块 from collections import deque def main(): info = deque((&q ...

  7. Python的程序结构[0] -> 属性/Property[0] -> 类属性、实例属性和私有属性

    类属性.实例属性和私有属性 Python中类的属性主要包括类属性,实例属性和私有属性,下面是对三种属性的简单介绍 类属性 / Class Property 类属性在__init__()之外初始化,在外 ...

  8. Python的程序结构[2] -> 类/Class[0] -> 类的特殊属性

    类的特殊属性 / Special Property of Class Python 中通过 class 进行类的定义,类可以实例化成实例并利用实例对方法进行调用. 类中还包含的一些共有的特殊属性. 特 ...

  9. Python的程序结构[2] -> 类/Class[1] -> 基类与继承

    基类与继承 / Base Class and Inheritance Class 面向对象的特性使得 Python 中不可避免地需要使用到类和类的继承,类的继承可以使得代码很好的被重用.下面以一些代码 ...

随机推荐

  1. php静态文件返回304

    有时一些静态文件(如图片)会由php输出,会发现请求都是200,静态文件每次都去服务器上请求太浪费资源了,这时如何让浏览器缓存图片呢?就需要我们在php中输出304了. 我们可以利用php中的 HTT ...

  2. android surfaceview 入门介绍

    由于工作中需自定义控件,以前没写过. 开始时,实用view 实现了,经理说不好,担心效率低,要求每秒需要刷新10次左右. 然后,学习使用  surfaceview. 看了网上简单的Demo,找到him ...

  3. B树、B-树、B+树、B*树 红黑树

    转载自:http://blog.csdn.net/quitepig/article/details/8041308 B树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): ...

  4. An internal error occurred during: "Launching on Tomcat 7.x"

    1.首先关闭MyEclipse工作空间.2.然后删除工作空间下的文件.“MyEclipse10\workspace.metadata.plugins\org.eclipse.core.runtime. ...

  5. Rbac_权限管理

    click!!! https://github.com/ugfly1210/rbac_100 有关于 rbac 的所有代码,包括 README. 用户和角色 : 多对多字段放在哪张表更好点? 用户找角 ...

  6. Opencv3.3.1安装包

    这个资源是Opencv3.3.1安装包,包括Windows软件包,Android软件包,IOS软件包,还有opencv的源代码:需要的下载吧. 点击下载

  7. shell之route命令相关

    使用下面的 route 命令可以查看 Linux 内核路由表. # route Destination Gateway Genmask Flags Metric Ref Use Iface 192.1 ...

  8. MVC4.0 bug 神奇的是事情 bool 值变成了 onclick ,非常奇怪的

    foreach (var item in ViewBag.PhotoGroupList) { // 这里很奇怪 item.IS_DISPLAY  是布尔值 如果直接写 @item.IS_DISPLAY ...

  9. Google C++编程风格指南 - 中文版

    Google C++编程风格指南 - 中文版 from http://code.google.com/p/google-styleguide/ 版本: 3.133原作者: Benjy Weinberg ...

  10. hibernate用注解的方式实现orm

    hibernate 有两种方式实现把一张表映射成一个对象,一种是配置文件的方式,一种是注解的方式.这里用hibernate提供的注解的方式实现一个对象和一张表之间的对应. 思路: 首先在hiberna ...