Python装饰器学习

    Python装饰器经常被用于切面需求,如插入日志、性能测试、事务处理等。装饰器是解决这类问题优选设计,使用装饰器可以从函数或者类中抽离出大量与函数功能本身无关的高频使用代码并重用它们。简言之装饰器的作用就是为已经存在的对象(函数或者类)添加额外的功能。

    

1  装饰器示例分析:

1.1 使用装饰器包装函数

(1)引例,不带参数和返回值

为了获取函数f执行时间,且要求不改变函数调用方式(参数列表也不能改变),为实现该目的:

1)我们需要一个封装函数f的函数并添加时间打印语句

2)为确保函数接口不变,我们要返回一个与函数f接口相同的函数

于是有下面代码出现:

import time
#原函数如下 
def foo():
    print 'test foo()'
 
# 定义外封装函数,传入待封装函数,并返回另一个附加计时功能的函数
def timef(func):
    # 定义一个内嵌的包装函数,给传入的函数加上计时功能,该函数参数接口同传入函数
    def wrapper():
        start = time.clock()
        func()
        end =time.clock()
        print 'time:', end - start
     
    # 将包装后的函数返回
    return wrapper
 
foo = timef(foo)
foo()

上面的示例使用python语法糖方式@调用封装函数,就是python包装器,如下所示:

import time
 
def timef(func):
    def wrapper():
        start = time.clock()
        func()
        end =time.clock()
        print 'time:', end - start
    return wrapper
 
@timef
def foo():
    print 'test foo()'
 
foo()

(2)带参数和返回值情形

def dec(func):
    def wrapper(a, b):
        print("before myfunc().")
        ret = func(a, b)
        print("after myfunc(),result: %s" % ret)
        return ret
    return wrapper
 
@dec
def add(a, b):
    print(" add(%s,%s) is called." % (a, b))
    return a + b
 
add(1, 2)


(3)参数个数不确定情形

def dec(func):
    def wrapper(*args, **kwargs):
        print("before %s." % func.__name__)
        ret = func(*args, **kwargs)
        print("after %s,result: %s" % (func.__name__, ret))
        return ret
    return wrapper
 
@dec
def add1(a, b):
    print(" add1(%s,%s) called." % (a, b))
    return a+b
 
@deco
def add2(a, b, c):
    print(" add2(%s,%s,%s) called." % (a, b, c))
    return a+b+c
 
add1(1, 2)
add2(1, 2, 3)


以上装饰器没有任何关于自己参数,以下是2个是装饰器有自己参数情形
(4)装饰器添加说明性参数

def pre_dec(arg):
    def dec(func):
        def _dec():
            print("before %s called [%s]." % (func.__name__, arg))
            func()
            print("  after %s called [%s]." % (func.__name__, arg))
        return _dec
    return dec
 
@pre_dec("mytest")
def func():
    print("func() called.")
 
func()


(5)装饰器使用类参数

这样可以在装饰器中使用类

class test:
    def __init__(self):
        print("test.__init__()not called")
         
    @staticmethod
    def cls_f1():
        print("test.cls_f1() called")
         
    @staticmethod
    def cls_f2():
        print("test.cls_f2() called")
 
def dec(cls):
    def _dec(func):
        def __dec():
            print("before %s called [%s]." % (func.__name__, cls))
            cls.cls_f1()
            try:
                return func()
            finally:
                cls.cls_f2()
        return __dec
    return _dec
 
@dec(test)
def myfunc():
    print("myfunc() called.")
 
myfunc()


(6)多装饰器顺序很重要

def f1(func):
    def wrapper():
        print("===f1 start===")
        func()
        print("===f1 end===")

    return wrapper


def f2(func):
    def wrapper():
        print("###f2 start###")
        func()
        print("###f2 end###")

    return wrapper
#交换f1和f2输出不一样,以下调用等价于test=f1(f2(test)),test()
@f1
@f2
def test(para="--:)--"):
    print para

test()


(7) 装饰器统一放到一个模块,其它模块通过导入该模块使用装饰器


file1:mydecorator.py

class test:
    def __init__(self):
        print("test.__init__()not called")
         
    @staticmethod
    def cls_f1():
        print("test.cls_f1() called")
         
    @staticmethod
    def cls_f2():
        print("test.cls_f2() called")
 
def dec(cls):
    def _dec(func):
        def __dec(args, kwargs):  #*args,**kwargs
            print("before %s called [%s]." % (func.__name__, cls))
            cls.cls_f1()
            try:
                return func(args, kwargs) #*args,**kargs
            finally:
                cls.cls_f2()
        return __dec
    return _dec
 

file2:app.py

from mydecorator import *
 
class example:
    @dec(test)
    def myfunc1(self):
        print(" myfunc1() called.")
 
    @dec(test)
    @dec(test)
    def myfunc2(self, x, y):
        print(" myfunc2() called.")
        return x + y
 
if __name__=="__main__":
    a = example()
    print(a.myfunc1())
    print(a.myfunc2(1, 2))
   


1.2 使用装饰器包装类

def dec(aclass):
    class new_class:
        def __init__(self,age):
            print("dec init")
            self.wrap = aclass(age)
        def display(self):
            print("dec display")
            self.wrap.display()
    return new_class

@dec
class Bird:
    def __init__(self,age):
        self.age = age
    def display(self):
        print("age is %s" % self.age)


a = Bird(2)
a.display()

2 内置装饰器

内置的装饰器有三个,分别是staticmethod、classmethod和property,作用分别是把类中定义的方法变成静态方法、类方法和类属性。

class test(object):
     
    def __init__(self, name):
        self._name = name
     
    @staticmethod
    def test1(name):
        return test(name)
     
    @classmethod
    def test2(cls):
        return test('')
     
    @property
    def name(self):
        return self._name
 
上面属性是只读,如要可写需要使用setter
@name.setter
def name(self, name):
    self._name = name

3 functools模块包含的2个装饰器

3.1 wraps(wrapped[, assigned][, updated]): 

这是一个很有用的装饰器。函数有几个特殊属性比如函数名,在被装饰后,上例中的函数名会变成包装函数的名字,这个装饰器能将装饰过的函数的特殊属性保留。

import time
import functools


def timef(func):
    @functools.wraps(func)
    def wrapper():
        start = time.clock()
        func()
        end = time.clock()
        print 'time:', end - start

    return wrapper


@timef
def foo():
    print 'test foo()'


foo()
print foo.__name__ #output foo,如果注释掉@functools.wraps(func)则输出wrapper

3.2  total_ordering(cls):

    它的作用是为实现了至少__lt__、__le__、__gt__、__ge__其中一个的类加上其他的比较方法,这是一个类装饰器。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值