python 学习笔记——python 装饰器(一)


装饰器的作用:常见的用法可以用来扩展一个方法(这个方法是其他的库里面的,你没办法修改)也可以用来方便调试(你不想修改原来的方法,只是想暂时看一下调试信息,之后就删掉了)

装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能


def benchmark(func):  
    """ 
    A decorator that prints the time a function takes 
    to execute. 
    一个输出函数运行时间的装饰器 
    """  
    import time  
    def wrapper(*args, **kwargs):  
        t = time.clock()  
        res = func(*args, **kwargs)  
        print func.__name__, time.clock()-t  
        return res  
    return wrapper  
  
  
def logging(func):  
    """ 
    A decorator that logs the activity of the script. 
    一个输出日志信息的装饰器 
    (it actually just prints it, but it could be logging!) 
    虽然这里只是简单得只用了print函数,但是你可以用其他日志模块代替 
    """  
    def wrapper(*args, **kwargs):  
        res = func(*args, **kwargs)  
        print func.__name__, args, kwargs  
        return res  
    return wrapper  
  
  
def counter(func):  
    """ 
    A decorator that counts and prints the number of times a function has been executed 
    一个记录、打印函数调用次数的装饰器 
    """  
    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  
 
@counter  
@benchmark  
@logging   #注意当有多个装饰器的时候执行的顺序是从内到外,装饰顺序为:@logging,@benchmark,@counter  先执行装饰器,最后执行被装饰函数体
def reverse_string(string):  
    return str(reversed(string))  
  
print reverse_string("Able was I ere I saw Elba")  
print reverse_string("A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!")  
  
#outputs:  
#reverse_string ('Able was I ere I saw Elba',) {}      此时装饰器中的func.__name__ 是被装饰的函数名
#wrapper 0.0                                          此时的装饰器的func.__name__ 是上层装饰器返回的wrapper
#wrapper has been used: 1x                             此时的装饰器的func.__name__ 是上层装饰器返回的wrapper
#ablE was I ere I saw elbA  
#reverse_string ('A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!',) {}  
#wrapper 0.0  
#wrapper has been used: 2x  
#!amanaP :lanac a ,noep a ,stah eros ,raj a ,hsac ,oloR a ,tur a ,mapS ,snip ,eperc a ,)


对带参数的函数进行装饰


# It's not black magic, you just have to let the wrapper   
# pass the argument:  
# 这一点都不神奇,只要让包装器(wrapper)传递参数就可以了  
  
def a_decorator_passing_arguments(function_to_decorate):  
    def a_wrapper_accepting_arguments(arg1, arg2):  
        print "I got args! Look:", arg1, arg2  
        function_to_decorate(arg1, arg2)  
    return a_wrapper_accepting_arguments  
  
# Since when you are calling the function returned by the decorator, you are  
# calling the wrapper, passing arguments to the wrapper will let it pass them to   
# the decorated function  
# 当你调用通过装饰器包装过后返回的函数时,   
# 相当于调用包装器,并且将参数传递给包装器,由包装器将参数传递给原始函数。  
 
@a_decorator_passing_arguments  
def print_full_name(first_name, last_name):  
    print "My name is", first_name, last_name  
  
print_full_name("Peter", "Venkman")  
# outputs:  
#I got args! Look: Peter Venkman  
#My name is Peter Venkman  


对参数数量不确定的函数进行装饰

def a_decorator_passing_arbitrary_arguments(function_to_decorate):  
    # The wrapper accepts any arguments  
    def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):  
        print "Do I have args?:"  
        print args  
        print kwargs  
        # Then you unpack the arguments, here *args, **kwargs  
        # If you are not familiar with unpacking, check:  
        # http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/  
        function_to_decorate(*args, **kwargs)  
    return a_wrapper_accepting_arbitrary_arguments  
 
@a_decorator_passing_arbitrary_arguments  
def function_with_no_argument():  
    print "Python is cool, no argument here."  
  
function_with_no_argument()  
#outputs  
#Do I have args?:  
#()  
#{}  
#Python is cool, no argument here.  
 
@a_decorator_passing_arbitrary_arguments  
def function_with_arguments(a, b, c):  
    print a, b, c  
  
function_with_arguments(1,2,3)  
#outputs  
#Do I have args?:  
#(1, 2, 3)  
#{}  
#1 2 3   
 
@a_decorator_passing_arbitrary_arguments  
def function_with_named_arguments(a, b, c, platypus="Why not ?"):  
    print "Do %s, %s and %s like platypus? %s" %\  
    (a, b, c, platypus)  
  
function_with_named_arguments("Bill", "Linus", "Steve", platypus="Indeed!")  
#outputs  
#Do I have args ? :  
#('Bill', 'Linus', 'Steve')  
#{'platypus': 'Indeed!'}  
#Do Bill, Linus and Steve like platypus? Indeed!  
  
class Mary(object):  
  
    def __init__(self):  
        self.age = 31  
 
    @a_decorator_passing_arbitrary_arguments  
    def sayYourAge(self, lie=-3): # You can now add a default value  
        print "I am %s, what did you think ?" % (self.age + lie)  
  
m = Mary()  
m.sayYourAge()  
#outputs  
# Do I have args?:  
#(<__main__.Mary object at 0xb7d303ac>,)  
#{}  
#I am 28, what did you think?  


装饰方法          #######  这里要注意的地方

What's great with Python is that methods and functions are really the same, except methods expect their first parameter to be a reference to the current object (self). It means you can build a decorator for methods the same way, just remember to take self in consideration:
在Python里面,方法(method)和函数(function)基本上是一样的,除了一点:方法的第一个参数必须是当前对象(self)的引用。也就是说你可以用同样的方法来装饰方法(method),只要记得处理self参数就可以了。 

def method_friendly_decorator(method_to_decorate):  
    def wrapper(self, lie):  
        lie = lie - 3 # very friendly, decrease age even more :-)  
        return method_to_decorate(self, lie)  
    return wrapper  
  
  
class Lucy(object):  
  
    def __init__(self):  
        self.age = 32  
 
    @method_friendly_decorator  
    def sayYourAge(self, lie):  
        print "I am %s, what did you think?" % (self.age + lie)  
  
l = Lucy()  
l.sayYourAge(-3)  
#outputs: I am 26, what did you think?  


让装饰器带参数


def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):    
    
#  一层封装,带参数是给装饰器的参数

    print "I make decorators! And I accept arguments:", decorator_arg1, decorator_arg2  
  
    def my_decorator(func):                      #  二层封装,带的参数是被装饰的函数
        # The ability to pass arguments here is a gift from closures.  
        # If you are not comfortable with closures, you can assume it's ok,  
        # or read: http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python  
        print "I am the decorator. Somehow you passed me arguments:", decorator_arg1, decorator_arg2  
  
        # Don't confuse decorator arguments and function arguments!  
        # 三层封装,对被封装的函数进行处理,此处的参数是被装饰函数的参数
        # 二三层的封装就是装饰器没有参数的时候(也就是外层装饰器的参数是被装饰的函数的那种情况)
        def wrapped(function_arg1, function_arg2) :                     
            print ("I am the wrapper around the decorated function.\n"  
                  "I can access all the variables\n"  
                  "\t- from the decorator: {0} {1}\n"  
                  "\t- from the function call: {2} {3}\n"  
                  "Then I can pass them to the decorated function"  
                  .format(decorator_arg1, decorator_arg2,  
                          function_arg1, function_arg2))  
            return func(function_arg1, function_arg2)  
  
        return wrapped  
  
    return my_decorator  
 
@decorator_maker_with_arguments("Leonard", "Sheldon")  
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("Rajesh", "Howard")  
#outputs:  
#I make decorators! And I accept arguments: Leonard Sheldon  
#I am the decorator. Somehow you passed me arguments: Leonard Sheldon  
#I am the wrapper around the decorated function.   
#I can access all the variables   
#   - from the decorator: Leonard Sheldon   
#   - from the function call: Rajesh Howard   
#Then I can pass them to the decorated function  
#I am the decorated function and only knows about my arguments: Rajesh Howard  

注意:此时需要装饰器要进行三层封装,比没有带参数的装饰器多了外层的一层封装functools 的使用# For debugging, the stacktrace prints you the function __name__  
def foo():  
    print "foo"  
  
print foo.__name__  
#outputs: foo  
  
# With a decorator, it gets messy      
def bar(func):  
    def wrapper():  
        print "bar"  
        return func()  
    return wrapper  
 
@bar  
def foo():  
    print "foo"  
  
print foo.__name__  
#outputs: wrapper  
  
# "functools" can help for that  
  
import functools  
  
def bar(func):  
    # We say that "wrapper", is wrapping "func"  
    # and the magic begins  
    @functools.wraps(func)  
    def wrapper():  
        print "bar"  
        return func()  
    return wrapper  
 
@bar  
def foo():  
    print "foo"  
  
print foo.__name__  
#outputs: foo  
1、They are new as of Python 2.4, so be sure that's what your code is running on.
2、Decorators slow down the function call. Keep that in mind.
3、You can not un-decorate a function. There are hacks to create decorators that can be removed but nobody uses them. So once a function is decorated, it's done. For all the code.
4、Decorators wrap functions, which can make them hard to debug.

1、装饰器是在Python 2.4之后才有的特性,所以请检查你的版本。
2、请记住:装饰器将会带来性能问题。
3、装饰是不可逆的。虽然已经有hacks设计出了可逆的装饰器,但是基本没人这么做。所以一旦一个函数被装饰过了,就无法还原了。
4、装饰器包装了函数,使得调试更加困难。
Python 2.5 solves this last issue by providing the functools module including functools.wraps that copies the name, module and docstring of any wrapped function to it's wrapper. Fun fact, functools.wraps is a decorator :-)
Python 2.5 解决上面提到的第四个问题。Python 2.5 之后,包含了一个functools模块,这个模块提供一个functools.wraps方法,这个方法将被包装的函数的name, module 和 docstring 都复制到包装好的函数上去。显然,functools.wraps也是一个装饰器 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值