博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Python中的函数修饰符 装饰器

Posted on 2017-06-29 16:56  bw_0927  阅读(228)  评论(0)    收藏  举报

http://blog.gusibi.com/post/python-decorator/

对一个已有的模块做一些“修饰工作”,所谓修饰工作就是想给现有的模块加上一些小装饰(一些小功能,这些小功能可能好多模块都会用到),但又不让这个小装饰(小功能)侵入到原有的模块中的代码里去

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()

# 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

 

8 多个装饰器

装饰器可以嵌套使用

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

8.0.1 outputs:

#嵌套两个装饰器

>> sandwich = bread(ingredients(sandwich))   //执行顺序,从左往右,先执行bread(), 把ingredients(sandwich)作为一个整体,当作bread()的参数
>> sandwich()

 

8.0.2 outputs:

```
</''''''\>
 #tomatoes#
 --ham--
 ~salad~
<\______/>
```

更简单的写法

@bread
@ingredients
def sandwich(food="--ham--"):
    print food

 

装饰器的顺序是很重要的

@ingredients
@bread
def sandwich(food="--ham--"):
    print food

# outputs:

```
 #tomatoes#
 </' ' ' ' ' '\>
   --ham--
 <\______/>
 ~salad~
```


9 Decorator 的本质

首先看一下这段代码

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)

其实就是把一个函数当参数传到另一个函数中,然后再回调 但是值得注意的是装饰器必须返回一个函数

========================

http://www.cnblogs.com/itech/archive/2011/12/31/2308766.html

 

装饰器有两种形式:

@A
def foo():
    pass

 

相当于:

def foo():
    pass
foo = A(foo)           #一次函数调用

 

第二种为带参数的:

@A(arg)
def foo():
    pass

 

则相当于:

def foo():
    pass
foo = A(arg)(foo)       #两次函数调用

 

可以看出第一种的装饰器是个返回函数的函数,第二种的装饰器是个返回函数的函数的函数。 

 

python中的decorator可以多个同时使用,如下:

@A
@B
@C
def f (): pass
    
# it is same as below
def f(): pass
f = A(B(C(f)))

 

 

三 Python中常用的decorator实例

decorator通常用来在执行前进行权限认证,日志记录,甚至修改传入参数,或者在执行后对返回结果进行预处理,甚至可以截断函数的执行等等。

 

实例1:

from functools import wraps
def logged(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print (func.__name__() + " was called")
        return func(*args, **kwargs)
    return with_logging

@logged
def f(x):
   """does some math"""
   return x + x * x

print (f.__name__)  # prints 'f'
print (f.__doc__)   # prints 'does some math'

注意functools.wraps()函数的作用:调用经过装饰的函数,例如foo = A(foo),调用装饰后的函数foo

相当于调用一个新函数,那查看函数参数,注释,甚至函数名的时候,就只能看到装饰器的相关信息,被包装函数的信息被丢掉了。

而wraps则可以帮你转移这些信息,参见http://stackoverflow.com/questions/308999/what-does-functools-wraps-do 

 

参考: 
http://www.cnblogs.com/Lifehacker/archive/2011/12/20/3_useful_python_decorator.html#2277951 

 

 

===========================================================

http://www.cnblogs.com/xupeizhi/archive/2013/02/07/2908600.html

http://coolshell.cn/articles/11265.html

http://www.artima.com/weblogs/viewpost.jsp?thread=240808

 

首先,什么是函数修饰符?函数修饰符就是对原有函数做一层包装。比如有以下两个函数:

  1. def func1():
  2.     print 'I am function func1'
  3. def func2():
  4.     print 'I am function func2'

现在我们想为这两个函数增加一段共同的操作,这时就可以使用函数修饰符。首先我们定义一个修饰函数,先不做任何处理,仅仅打印一条横线:

def de( f )
    def call_():
        print '-------------------------------'     //新功能
        return f()       //原始功能
    return _call_       //返回函数对象


使用这个修饰符:

def de( f ):
    def _call_():
        print '-------------------------------'
        return f()
    return _call

@de
def func1():
    print 'I am function func1'

@de
def func2():
    print 'I am function func2'

if name = '__main__':
    func1()
    func2()

  

运行结果为:

-------------------------------

I am function func1

-------------------------------

I am function func2

 

可以看到,修饰符起作用了。 

注意: 
1.修饰符等价于包装调用:

@de

def func1:

等价于 

func1 = de( func1 )    //原始功能func1被作为参数传递, 同时执行de()函数

 

2.修饰函数必须返回一个“可调用对象”:

def de( f ):

    def call_():

        return f()

    return _call    # 返回的是一个函数体,而非调用_call_()

 


下一个问题:如果各个被修饰函数有不同的参数,怎么处理呢?例如:

def func1( lst1, lst2 ):
    # 合并两个list,并打印各项
    for item in lst1+lst2:
        print item
def func2( dic ):
    # 循环dict,打印各key-value
    for k, v in dic.items():
        print k, v

这两个函数参数不同,又都没有异常处理,现在我们想加上异常处理,使用修饰符这样处理:

import sys
import traceback
def de( f ):
    def _call( *args, **kwargs ):
        try:
            return f( *args , **kwargs )
        except:
            print 'param type error'
    return _call
@de def func1( lst1, lst2 ): for item in lst1+lst2: print item @de def func2( dic ): for k, v in dic.items(): print k, v
if __name__ == '__main__': func1( [1,2], [3,4] ) func2( [1,2] )  

这里使用了可变参数*args和**kwargs,这样会把所有接收来的参数,原样不动的再转给原函数,是惯用法。 


以上程序运行结果: 

1

2

3

4

param type error    //因为func2的参数是一个dic,而实参[1, 2]是一个list

 

 

==========

需要注意的:

1. 函数先定义,再修饰它;反之会编译器不认识;

2. 修饰符“@”后面必须是之前定义的某一个函数;

3. 每个函数只能有一个修饰符,大于等于两个则不可以。

 

 

from functools import wraps

def my_decorator(f):
    @wraps(f)
    def wrapper(*args, **kwds):
        print 'Calling decorated function'
        return f(*args, **kwds)
    return wrapper

@my_decorator
def example():
    """Docstring"""
    print 'Called example function'

example()
print example.__name__
print example.__doc__

输出:

Calling decorated function
Called example function
example
Docstring

  

代码使用 @functools.wraps 装饰将要返回的包裹函数 wrapper,使得他的 __name__, __doc__ 和 __module__ 属性与被装饰函数 examle()完全相同, 这样虽然最终调用的是经过装饰的 example() 函数,但是某些属性还是得到了维护。

 

如果不使用  @wraps(f) , 输出结果:

Calling decorated function
Called example function
example
None