我跟别人说我精通python,别人问我wrapper是啥,我说不知道,尼玛,原来wrapper就是装饰器,熟的不得了啊,英语真是我的克星啊。

闭包 closure

在认识装饰器之前先认识下闭包

闭包,顾名思义就是把什么东西封闭在保内,什么东西呢?变量和函数。

在一个函数里装了另一个函数,里面那个函数称为内部函数,外面那个函数称为外部函数,

在内部函数里,对在外部作用域(非全局作用域)里的变量进行引用,这个内部函数就称为闭包

定义在外部函数内但被内部函数引用或调用的变量称为自由变量,所以闭包又被称为引用了自由变量的函数

内部函数和自由变量同时存在构建一个闭包实体,闭包的作用就是使闭包实体可以脱离创建它的环境运行,就是变量和函数脱离了创建它的环境依然存在,而且可执行,这点跟面向对象有些类似。

闭包的语法是一个函数A内创建了一个函数B,并且函数A返回了函数B,函数B就是闭包,函数A内的变量叫自由变量,包括A的参数

示例代码

def counter(start_at=):
print('act')
count=[start_at] # 自由变量
def incr(): # 内部函数
count[]+=
return count[]
return incr # 返回一个函数对象 count=counter() # act
print #
print count() # # count 和 incr 脱离了创建它的环境,依然可以运行
print count() # count2=counter() # act
print #
print count2() #
print count() #

闭包的作用

1. 闭包实际上是对数据或者数据操作的封装

2. 闭包可以实现一些通用的功能,它是装饰器的基础。

装饰器

装饰器本质上是个函数,一个用来包装函数的函数,返回被包装的函数对象。

被包装的函数需要作为装饰器函数的参数。

装饰器以语法糖@开头,形式如下

@decorator(dec_opt_args)
def func2Bdecorated(func_opt_args):

并且可以有多个装饰器

@deco2
@deco1
def func():
pass 等价于 func=deco2(deco1(func))

普通方式实现类似装饰器的功能,以帮助理解

def deco(func):
print("before myfunc() called.")
func()
print(" after myfunc() called.")
return func def myfunc():
print(" myfunc() called.") myfunc = deco(myfunc)
# before myfunc() called.
# myfunc() called.
# after myfunc() called. myfunc() # myfunc() called.
myfunc() # myfunc() called.

使用语法糖@装饰函数,注意这并不是装饰器

def deco(func):
print("before myfunc() called.")
func()
print("after myfunc() called.")
return func @deco
def myfunc():
print("myfunc() called.") myfunc()
# before myfunc() called.
# myfunc() called.
# after myfunc() called.
# myfunc() called.
print("*"*5)
myfunc() # myfunc() called.

可以看到第一次执行时@起了作用,第二次执行时@没起作用。为什么呢,往下看

真正的装饰器来了

无参装饰器

def deco(func):
print(33)
def _deco():
print("before myfunc() called.")
func()
print(" after myfunc() called.")
return _deco @deco
def myfunc():
print(" myfunc() called.")
return 'ok' myfunc()
#
# before myfunc() called.
# myfunc() called.
# after myfunc() called.
myfunc()
# before myfunc() called.
# myfunc() called.
# after myfunc() called.

可以看到每次执行@都起了作用,但是33只被打印了一次,又是为什么呢?

明明是执行了两次一模一样的操作,结果却不同,看来有必要深入理解一下装饰器了。

深入理解装饰器

之前讲到闭包类似于面向对象,而装饰器基于闭包,也应该类似于面向对象咯,或许吧,类似嘛,我又没说是,所以应该没错,

为什么扯这么多,因为我要用class来解释上面的问题。

对上面的无参装饰器分析一

## 上述过程类似这样
def myfunc():
print(" myfunc() called.")
return 'ok' bb = deco(myfunc) #
bb()
# before myfunc() called.
# myfunc() called.
# after myfunc() called.
bb()
# before myfunc() called.
# myfunc() called.
# after myfunc() called.

把装饰器转成普通函数,就明了了:

装饰器内的操作在创建装饰器实例时已经运行,这可以理解为class的实例化,如果在实例化时有print操作,在实例调用时不会再有

对上面的无参装饰器分析二

def deco(func):
a = 3
def _deco():
print("before myfunc() called.")
func()
print(a)
print(" after myfunc() called.")
return _deco @deco
def myfunc():
print(" myfunc() called.")
return 'ok' myfunc()
# before myfunc() called.
# myfunc() called.
#
# after myfunc() called.
myfunc()
# before myfunc() called.
# myfunc() called.
#
# after myfunc() called.

装饰器传递的是自由变量和闭包,可以理解为class的实例属性和方法,在实例调用时,属性一直存在。

myfunc()第一次运行时相当于初始化了装饰器,后面只是调用实例,虽然它没有生成实例对象,在这点上不同于class。

总结

装饰器函数真的类似于面向对象

装饰器在第一次运行时相当于实例化class,实例化时可以有操作和属性,操作不被传递,属性被传递

装饰器不需要创建实例对象,运行即相当于实例化class

装饰器传递的是自由变量和属性,装饰器函数内的操作不被传递

装饰器的各种语法

有参装饰器

def deco(func):
def _deco(a, b):
print("before myfunc() called.")
ret = func(a, b)
print(" after myfunc() called. result: %s" % ret)
return ret
return _deco @deco
def myfunc(a, b):
print(" myfunc(%s,%s) called." % (a, b))
return a + b myfunc(1, 2)
# before myfunc() called.
# myfunc(1,2) called.
# after myfunc() called. result: 3
myfunc(3, 4)
# before myfunc() called.
# myfunc(3,4) called.
# after myfunc() called. result: 7

装饰器带参数

外面加一层

def deco(arg):
def _deco(func):
def __deco():
print("before %s called [%s]." % (func.__name__, arg))
func()
print(" after %s called [%s]." % (func.__name__, arg))
return __deco
return _deco @deco("mymodule")
def myfunc():
print(" myfunc() called.") @deco("module2")
def myfunc2():
print(" myfunc2() called.") myfunc()
myfunc2()

装饰器带类参数

装饰器的参数是个类,也可以是实例,或者其他

class locker:
def __init__(self):
print("locker.__init__() should be not called.") @staticmethod
def acquire():
print("locker.acquire() called.(这是静态方法)") @staticmethod
def release():
print(" locker.release() called.(不需要对象实例)") def deco(cls):
'''cls 必须实现acquire和release静态方法'''
def _deco(func):
def __deco():
print("before %s called [%s]." % (func.__name__, cls))
cls.acquire()
try:
return func()
finally:
cls.release()
return __deco
return _deco @deco(locker)
def myfunc():
print(" myfunc() called.") myfunc()
myfunc()

被装饰的函数属性发生了变化

def deco(func):
def myfunc(x):
return func(x)
return myfunc @deco
def test1(x):
return x+1 print(test1(4)) #
print(test1.__name__) # myfunc 名字并非真实名字

名字并非真正函数的名字,而是装饰器函数里被装饰的函数的名字

保留被装饰的函数的属性

import time
import functools def timeit(func):
@functools.wraps(func) # 此句就是用来保留被装饰的函数的属性的 ,其余跟普通装饰器一样
def wrapper():
start = time.clock()
func()
end =time.clock()
print 'used:', end - start
return wrapper @timeit
def foo():
print 'in foo()' foo() # used: 5.13182184074e-06
print foo.__name__ # foo

name属性被保留

参考资料:

http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html

装饰器-wrapper的更多相关文章

  1. python14 1.带参装饰器 | wrapper 了了解 # 2.迭代器 ***** # 可迭代对象 # 迭代器对象 # for迭代器 # 枚举对象

    ## 复习 '''函数的嵌套定义:在函数内部定义另一个函数 闭包:被嵌套的函数 -- 1.外层通过形参给内层函数传参 -- 2.验证执行 开放封闭原则: 功能可以拓展,但源代码与调用方式都不可以改变 ...

  2. day14带参装饰器,迭代器,可迭代对象 , 迭代器对象 ,for迭代器 , 枚举对象

    复习 ''' 函数的嵌套定义:在函数内部定义另一个函数 闭包:被嵌套的函数 -- 1.外层通过形参给内层函数传参 -- 2.验证执行 开放封闭原则: 功能可以拓展,但源代码与调用方式都不可以改变 装饰 ...

  3. python学习日记(函数--装饰器)

    楔子 前提,我有一段代码(一个函数). import time def run_time(): time.sleep(0.1) print('我曾踏足山巅') 需求1:现在,我想计算这段代码的运行时间 ...

  4. python当中的装饰器

    1.装饰器 首先我们来说一下一个软件的设计原则:开闭原则,又被称为开发封闭原则,你的代码对功能的扩展是开放的,你的程序对修改源代码是封闭的.这样的软件设计思路可以更好的维护和开发. 开放:对功能扩展开 ...

  5. python基础(8)--迭代器、生成器、装饰器

    1.迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退.另外,迭代器的一大优 ...

  6. Python入门-装饰器初始

    今天我们就围绕一个来展开,那就是:装饰器 一.装饰器 在说装饰器之前,我们先说一个软件设计的原则:开闭原则,又被称为开放封闭原则,你的代码对功能的扩展是开放的,你的程序对修改源代码是封闭的,这样的软件 ...

  7. 巨蟒python全栈开发-第15天 装饰器

    一.今日内容总览 关于函数的装饰器1.装饰器(重点,难点)(要求:反复写,代码不多但是很绕) 开闭原则:(比如,菜单是拆散的,一点点搞的,用友拆散自己的功能,以后就不用开发了) (1)对功能的扩展开放 ...

  8. python 复习函数 装饰器

    # 函数 —— 2天 # 函数的定义和调用 # def 函数名(形参): #函数体 #return 返回值 #调用 函数名(实参) # 站在形参的角度上 : 位置参数,*args,默认参数(陷阱),* ...

  9. 13.Python略有小成(装饰器,递归函数)

    Python(装饰器,递归函数) 一.开放封闭原则 ​ 软件面世时,不可能把所有的功能都设计好,再未来的一两年功能会陆续上线,定期更新迭代,软件之前所用的源代码,函数里面的代码以及函数的调用方式一般不 ...

随机推荐

  1. LeetCode--003--无重复字符的最长子串(java)

    给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc&qu ...

  2. 框架中如何根据fileupload工具包实现文件上传功能

    工具包 Apache-fileupload.jar – 文件上传核心包. Apache-commons-io.jar – 这个包是fileupload的依赖包.同时又是一个工具包. 代码 servle ...

  3. linux常用网络命令ping和arping

    linux常用网络命令ping和arping ping 向目标主机发送icmp请求包 常用来测试当前主机与目标主机网络连接状况 常见选项 -c              设置发包的个数 -s      ...

  4. python记录_day03 字符串

    python基本数据类型回顾 1. int 整数 2. str 字符串. 不会用字符串保存大量的数据 3. bool 布尔值. True, False 4. list 列表(重点) 存放大量的数据 5 ...

  5. Educational Codeforces Round 48 (Rated for Div. 2)G. Appropriate Team

    题意:求满足条件的(i,j)对数:\(gcd(v,a_i)=x,lcm(v,a_j)=y\) 题解:\(x|a_i,a_j|y\),\(x|y\),考虑质因子p,假设a_i中p次数为a,x中次数为b, ...

  6. 【oauth2.0】【2】JAVA 客户端模式

    含义:用户直接向客户端注册,客户端以自己的名义要求"服务提供商"提供服务,其实不存在授权问题 步骤: (A)客户端向认证服务器进行身份认证,并要求一个访问令牌(token). (B ...

  7. 详解 java socket

    一,网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输. 在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可 ...

  8. hdu 3591 多重加完全DP

    题目: The trouble of Xiaoqian Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (J ...

  9. java集合类整理

    LinkedList 优点:插入删除迅速 缺点:不适合随机访问 List<String> staff = new LinkedList<String>(); staff.add ...

  10. [Codeforces Round #340 (Div. 2)]

    [Codeforces Round #340 (Div. 2)] vp了一场cf..(打不了深夜的场啊!!) A.Elephant 水题,直接贪心,能用5步走5步. B.Chocolate 乘法原理计 ...