装饰器是可调用的对象,其参数是另外一个函数(被装饰的函数)。html
首先看一下这段代码python
def deco(fn): print "I am %s!" % fn.__name__ @deco def func(): pass # output I am func! # 没有执行func 函数 可是 deco 被执行了
在用某个@decorator来修饰某个函数func时git
@decorator def func(): pass
其解释器会解释成下面这样的语句:github
func = decorator(func)
shell
其实就是把一个函数当参数传到另外一个函数中,而后再回调,可是值得注意的是装饰器必须返回一个函数给func编程
装饰器的一大特性是,能把被装饰的函数替换成其余函数。第二大特性是,装饰器在加载模块时当即执行。flask
装饰器的一个关键特性是,它们在被装饰的函数定义后当即运行。这一般在导入是(python 加载模块时)。设计模式
看下下面的示例:数组
registry = [] # registry 保存被@register 装饰的函数的引用 def register(func): # register 的参数是一个函数 print('running register(%s)' % func) # 打印被装饰的函数 registry.append(func) # 把 func 存入 `registery` return func # 返回 func:必须返回函数,这里返回的函数与经过参数传入的同样 @register # `f1` 和 `f2`被 `@register` 装饰 def f1(): print('running f1()') @register def f2(): print('running f2()') def f3(): # <7> print('running f3()') def main(): # main 打印 `registry`,而后调用 f1()、f2()和 f3() print('running main()') print('registry ->', registry) f1() f2() f3() if __name__=='__main__': main() # <9>
运行代码结果以下:缓存
running register(<function f1 at 0x1023fb378>) running register(<function f2 at 0x1023fb400>) running main() registry -> [<function f1 at 0x1023fb378>, <function f2 at 0x1023fb400>] running f1() running f2() running f3()
从结果能够发现register
在模块中其余函数以前运行了两次。调用 register 时,传给它的参数是被装饰的函数(例如<function f1 at 0x1023fb378>)。
看完上边的示例咱们知道,函数被装饰器装饰后会变成装饰器函数的一个参数,那这时就不得不说变量的做用域了。
先看下下边这段代码:
def f1(a): print(locals()) print(a) print(b) f1(3) # output {'a': 3} 3 Traceback(most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in f1 NameError: global name 'b' is not defined
这里的错误是由于全局变量 b
没有定义,若是咱们先在函数外部给 b 赋值,再调用这个方法就不会报错了。
函数运行时会建立一个新的做用域(命名空间)。函数的命名空间随着函数调用开始而开始,结束而销毁。
这个例子中 f1 的命名空间中只有 {'a': 3},因此b
会被认为是全局变量。
再看一个例子:
b = 6 def f2(a): print(a) print(globals()) print(locals()) print(b) b = 9 f2(3) # output 3 { '__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x10c7f2dd8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '~/var_local.py', '__cached__': None, 'b': 6, 'f2': <function f2 at 0x10c7e7598> } {'a': 3} 3 Traceback(most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in f1 UnboundLocalError: local variable 'b' referenced before assignment
这个例子和上一个例子不一样是,我如今函数外部定义了全局变量b
,可是执行f2
这个方法并无打印6,这是为何呢?
这是由于执行函数时 Python 会尝试从局部变量中获取 b
,函数对于已经引用但未赋值的变量并不会自动声明为局部变量,因此解释器发现后边的赋值以前有引用就会抛出 UnboundLocalError
错误。
Python 不要求声明变量,可是假定在函数定义体中赋值的变量是局部变量。
若是要让解释器把b
当作全局变量,要使用global
声明:
b = 6 def f3(a): global b print(a) print(b) b = 9 f2(3) # output 3 6
闭包
是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义做用域不可用,但仍能使用那些绑定。
介绍闭包前先要说明一下 Python 的函数参数
函数有两种参数
位置参数
命名参数
def foo(x, y=0): return x - y
函数和python中其余同样都是对象
In [7]: class A(object): ...: pass In [8]: A Out[8]: __main__.A In [9]: type(A) Out[9]: type In [10]: def foo(): ....: pass In [11]: type(foo) Out[11]: function In [12]: A.__class__ Out[12]: type In [13]: foo.__class__ Out[13]: function In [14]: a = 1 In [15]: a.__class__ Out[15]: int # 类 是对象 In [16]: issubclass(A.__class__, object) Out[16]: True # 变量 是对象 In [17]: issubclass(a.__class__, object) Out[17]: True # 函数 是对象 In [18]: issubclass(foo.__class__, object) Out[18]: True
因此函数也能够做为参数传递给其它函数,也能够被当作返回值返回
def add(x, y): return x + y def apply(func): return func >> a = apply(add) >> type(a) <type 'function'> >> a(1, 2) >> 3
先来看一个示例:假设有个名为 avg 的函数,它的做用是计算不断增长的系列值的均值;
它是这么使用的:
>>> avg(10) 10 >>> avg(11) 10.5 >>> avg(12) 11
那么咱们考虑下,avg 从何而来,它又在哪里保存历史值呢,这个用闭包如何实现呢?
下边的代码是闭包的实现:
def make_averager(): series = [] def averager(new_value): series.append(new_value) total = sum(series) return total/len(series) return averager
调用 make_averager
时,返回一个 averager 函数对象。每次调用 averager 时,它都会把参数添加到系列值中,而后计算当前平均值。
avg = make_averager() >>> avg(10) 10 >>> avg(11) 10.5 >>> avg(12) 11
series
是make_averager
函数的局部变量,由于那个函数的定义体中初始化了series: series=[]
。但在averager
函数中,series
是自由变量(指未在本地做用域中绑定的变量)。
averager
的闭包延伸到那个函数的做用域以外,包含自由变量series
的绑定。
avg 就是一个闭包
也能够说 make_averager 指向一个闭包
或者说 make_averager 是闭包的工厂函数
闭包能够认为是一个内层函数(averager),由一个变量指代,而这个变量相对于外层包含它的函数而言,是本地变量
嵌套定义在非全局做用域里面的函数可以记住它在被定义的时候它所处的封闭命名空间
闭包
只是在形式和表现上像函数,但实际上不是函数。函数是一些可执行的代码,这些代码在函数被定义后就肯定了,不会在执行时发生变化,因此一个函数只有一个实例。闭包在运行时能够有多个实例,不一样的引用环境和相同的函数组合能够产生不一样的实例。
对一个已有的模块作一些“修饰工做”,所谓修饰工做就是想给现有的模块加上一些小装饰(一些小功能,这些小功能可能好多模块都会用到),但又不让这个小装饰(小功能)侵入到原有的模块中的代码里去
def my_decorator(func): def wrapper(): print "Before the function runs" func() # 这行代码可用,是由于 wrapper 的闭包中包含自由变量 func print "After the function runs" return wrapper def my_func(): print "I am a stand alone function" >> my_func() # output I am a stand alone function # 而后,咱们在这里装饰这个函数 # 将函数传递给装饰器,装饰器将动态地将其包装在任何想执行的代码中,而后返回一个新的函数 >> my_func = my_decorator(my_func) >> my_func() #output Before the function runs I am a stand alone function After the function runs # 也能够这么写 @ my_decorator def my_func(): print "I am a stand alone function" >> my_func() #output Before the function runs I am a stand alone function After the function runs
装饰器能够嵌套使用
def bread(func): def wrapper(): print "</''''''\>" func() print "<\______/>" return wrapper def ingredients(func): def wrapper(): print "#tomatoes#" func() print "~salad~" return wrapper def sandwich(food="--ham--"): print food #### outputs:
>> sandwich = bread(ingredients(sandwich)) >> sandwich() #### outputs </''''''\> #tomatoes# --ham-- ~salad~ <\______/>
更简单的写法
@bread @ingredients def sandwich(food="--ham--"): print food
装饰器的顺序是很重要的
若是咱们换下顺序就会发现,三明治变成了披萨。。
@ingredients @bread def sandwich(food="--ham--"): print food # outputs: #tomatoes# </' ' ' ' ' '\> --ham-- <\______/> ~salad~
首先看一下这段代码
def deco(fn): print "I am %s!" % fn.__name__ @deco def func(): pass # output I am func! # 没有执行func 函数 可是 deco 被执行了
在用某个@decorator来修饰某个函数func时
@decorator def func(): pass
其解释器会解释成下面这样的语句:
func = decorator(func)
其实就是把一个函数当参数传到另外一个函数中,而后再回调
可是值得注意的是装饰器必须返回一个函数给func
回到刚才的例子
def my_decorator(func): def wrapper(): print "Before the function runs" func() print "After the function runs" return wrapper def my_func(): print "I am a stand alone function" >> my_func = my_decorator(my_func) >> my_func() #output Before the function runs I am a stand alone function After the function runs
my_decorator(my_func)返回了wrapper()函数,因此,my_func其实变成了wrapper的一个变量,然后面的my_func()执行其实变成了wrapper()
好比:多个decorator
@decorator_one @decorator_two def func(): pass
至关于:
func = decorator_one(decorator_two(func))
好比:带参数的decorator:
@decorator(arg1, arg2) def func(): pass # 至关于: func = decorator(arg1,arg2)(func)
首先看一下, 若是被装饰的方法有参数
def a_decorator(method_to_decorate): def wrapper(self, x): x -= 3 print 'x is %s' % x method_to_decorate(self, x) return wrapper class A(object): def __init__(self): self.b = 42 @a_decorator def number(self, x): print "b is %s" % (self.b + x) a = A() a.number(-3) # output x is -6 b is 36
一般咱们都使用更加通用的装饰器,能够做用在任何函数或对象方法上,而没必要关心其参数使用
def a_decorator(method_to_decorate): def wrapper(*args, **kwargs): print '****** args ******' print args print kwargs method_to_decorate(*args, **kwargs) return wrapper @a_decorator def func(): pass func() #output ****** args ****** () {} @a_decorator def func_with_args(a, b=0): pass return a + b func_with_args(1, b=2) #output ****** args ****** (1,) {'b': 2}
上边的示例是带参数的被装饰函数
如今咱们看一下向装饰器自己传递参数
装饰器必须使用函数做为参数,你不能直接传递参数给装饰器自己
若是想传递参数给装饰器,能够 声明一个用于建立装饰器的函数
# 我是一个建立装饰器的函数 def decorator_maker(): print "I make decorators!" def my_decorator(func): print "I am a decorator!" def wrapped(): print "I am the wrapper around the decorated function. " return func() print "As the decorator, I return the wrapped function." return wrapped print "As a decorator maker, I return a decorator" return my_decorator # decorator_maker()返回的是一个装饰器 new_deco = decorator_maker() #outputs I make decorators! As a decorator maker, I return a decorator # 使用装饰器 def decorated_function(): print "I am the decorated function" decorated_function = new_deco(decorated_function) decorated_function() # outputs I make decorators! As a decorator maker, I return a decorator I am a decorator! As the decorator, I return the wrapped function. I am the wrapper around the decorated function. I am the decorated function
使用@修饰
decorated_function = new_deco(decorated_function) # 等价于下面的方法 @new_deco def func(): print "I am the decorated function" @decorator_maker() def func(): print "I am the decorated function"
my_decorator(装饰器函数)是decorator_maker(装饰器生成函数)的内部函数
因此可使用把参数加在decorator_maker(装饰器生成函数)的方法像装饰器传递参数
# 我是一个建立带参数装饰器的函数 def decorator_maker_with_arguments(darg1, darg2): print "I make decorators! And I accept arguments:", darg1, darg2 def my_decorator(func): print "I am a decorator! Somehow you passed me arguments:", darg1, darg2 def wrapped(farg1, farg2): print "I am the wrapper around the decorated function." print "I can access all the variables", darg1, darg2, farg1, farg2 return func(farg1, farg2) print "As the decorator, I return the wrapped function." return wrapped print "As a decorator maker, I return a decorator" return my_decorator @decorator_maker_with_arguments("deco_arg1", "deco_arg2") def decorated_function_with_arguments(function_arg1, function_arg2): print ("I am the decorated function and only knows about my arguments: {0}" " {1}".format(function_arg1, function_arg2)) decorated_function_with_arguments('farg1', 'farg2') # outputs I make decorators! And I accept arguments: deco_arg1 deco_arg2 As a decorator maker, I return a decorator I am a decorator! Somehow you passed me arguments: deco_arg1 deco_arg2 As the decorator, I return the wrapped function. I am the wrapper around the decorated function. I can access all the variables deco_arg1 deco_arg2 farg1 farg2 I am the decorated function and only knows about my arguments: farg1 farg2
这里装饰器生成函数内部传递参数是闭包的特性
装饰器是Python2.4的新特性
装饰器会下降代码的性能
装饰器仅在Python代码导入时被调用一次,以后你不能动态地改变参数.当你使用"import x",函数已经被装饰
functools.wraps
最后Python2.5解决了最后一个问题,它提供functools
模块,包含functools.wraps
,这个函数会将被装饰函数的名称、模块、文档字符串拷贝给封装函数
def foo(): print "foo" print foo.__name__ #outputs: foo # 但当你使用装饰器 def bar(func): def wrapper(): print "bar" return func() return wrapper @bar def foo(): print "foo" print foo.__name__ #outputs: wrapper
"functools" 能够修正这个错误
import functools def bar(func): # 咱们所说的 "wrapper", 封装 "func" @functools.wraps(func) def wrapper(): print "bar" return func() return wrapper @bar def foo(): print "foo" # 获得的是原始的名称, 而不是封装器的名称 print foo.__name__ #outputs: foo
class myDecorator(object): def __init__(self, func): print "inside myDecorator.__init__()" self.func = func def __call__(self): self.func() print "inside myDecorator.__call__()" @myDecorator def aFunction(): print "inside aFunction()" print "Finished decorating aFunction()" aFunction() # output: # inside myDecorator.__init__() # Finished decorating aFunction() # inside aFunction() # inside myDecorator.__call__()
咱们能够看到这个类中有两个成员:
一个是__init__(),这个方法是在咱们给某个函数decorator时被调用,因此,须要有一个func的参数,也就是被decorator的函数。
一个是__call__(),这个方法是在咱们调用被decorator函数时被调用的
若是decorator有参数的话,__init__() 就不能传入func了,而fn是在__call__的时候传入
class myDecorator(object): def __init__(self, arg1, arg2): self.arg1 = arg2 def __call__(self, func): def wrapped(*args, **kwargs): return self.func(*args, **kwargs) return wrapped
Python 内置了三个用于装饰方法的函数:property、classmethod和 staticmethod。
另外一个常见的装饰器是 functools.wraps,它的做用是协助构建行为良好的装饰器。
functools.lru_cache
实现了内存缓存功能,它能够把耗时长的函数结果保存起来,避免传入相同参数时重复计算。
咱们本身的实现代码以下:
from functools import wraps def memo(fn): cache = {} miss = object() @wraps(fn) def wrapper(*args): result = cache.get(args, miss) if result is miss: result = fn(*args) print "{0} has been used: {1}x".format(fn.__name__, wrapper.count) cache[args] = result return result return wrapper @memo def fib(n): if n < 2: return n return fib(n - 1) + fib(n - 2)
def counter(func): """ 记录并打印一个函数的执行次数 """ def wrapper(*args, **kwargs): wrapper.count = wrapper.count + 1 res = func(*args, **kwargs) print "{0} has been used: {1}x".format(func.__name__, wrapper.count) return res wrapper.count = 0 return wrapper
装饰器作缓存
def cache_for(duration): def deco(func): @wraps(func) def fn(*args, **kwargs): key = pickle.dumps((args, kwargs)) value, expire = func.func_dict.get(key, (None, None)) now = int(time.time()) if value is not None and expire > now: return value value = func(*args, **kwargs) func.func_dict[key] = (value, int(time.time()) + duration) return value return fn return deco
def timeit(fn): @wraps(fn) def real_fn(*args, **kwargs): if config.common['ENVIRON'] == 'PRODUCTION': return fn(*args, **kwargs) _start = time.time() #app.logger.debug('Start timeit for %s' % fn.__name__) result = fn(*args, **kwargs) _end = time.time() _last = _end - _start app.logger.debug('End timeit for %s in %s seconds.' % (fn.__name__, _last)) return result return real_fn
[args and *kwargs? [duplicate]](http://stackoverflow.com/ques...
最后,感谢女友支持。
欢迎关注(April_Louisa) | 请我喝芬达 |
---|---|
![]() |
![]() |