我跟别人说我精通python,别人问我wrapper是啥,我说不知道,尼玛,原来wrapper就是装饰器,熟的不得了啊,英语真是我的克星啊。
闭包 closure
在认识装饰器之前先认识下闭包
闭包,顾名思义就是把什么东西封闭在保内,什么东西呢?变量和函数。
在一个函数里装了另一个函数,里面那个函数称为内部函数,外面那个函数称为外部函数,
在内部函数里,对在外部作用域(非全局作用域)里的变量进行引用,这个内部函数就称为闭包
定义在外部函数内但被内部函数引用或调用的变量称为自由变量,所以闭包又被称为引用了自由变量的函数。
内部函数和自由变量同时存在构建一个闭包实体,闭包的作用就是使闭包实体可以脱离创建它的环境运行,就是变量和函数脱离了创建它的环境依然存在,而且可执行,这点跟面向对象有些类似。
闭包的语法是一个函数A内创建了一个函数B,并且函数A返回了函数B,函数B就是闭包,函数A内的变量叫自由变量,包括A的参数
示例代码
def counter(start_at=0): print('act') count=[start_at] # 自由变量 def incr(): # 内部函数 count[0]+=1 return count[0] return incr # 返回一个函数对象 count=counter(5) # act print 111 # 111 print count() # 6 # count 和 incr 脱离了创建它的环境,依然可以运行 print count() # 7 count2=counter(100) # act print 222 # 222 print count2() # 101 print count() # 8
闭包的作用
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() # 33 # 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) # 3 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. # 3 # after myfunc() called. myfunc() # before myfunc() called. # myfunc() called. # 3 # 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)) # 5 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