python装饰器

Python2.4开始提供装饰器(decorator),适当使用装饰器可以有效提高代码可读性和可维护性。装饰器的本质就是一个函数,这个函数接受其他函数作为参数,并将其以一个新的修改后的函数进行替换。

一、嵌套函数
一个函数定义在另外一个函数中。

def outer(x,y):
    def inner():
        return x+y
    return inner
f = outer(1,3)
print(f())

调用f函数时,实际调用的是inner函数。

二、装饰器的原型

def say_hi():
    print("hi")

def bread(f):
    def wrapper(*args, **kwargs):
        print("begin call {0}".format(f.__name__))
        f(*args, **kwargs)
        print("finish call {0}".format(f.__name__))
    return wrapper

bread(say_hi())

bread函数接收say_hi作为参数,wrapper嵌套函数首先打印begin消息,然后执行say_hi函数,在执行finsh消息。最后将嵌套函数wrapper作为返回值,返回给了bread函数的调用者。bread函数调用者得到返回值后,执行函数调用。

使用Python语法糖改造一下,就变成了一个装饰器定义和使用的完整例子。

def bread(f):
    def wrapper(*args, **kwargs):
        print("begin call {0}".format(f.__name__))
        f(*args, **kwargs)
        print("finish call {0}".format(f.__name__))
    return wrapper

@bread
def say_hi():
    print("hi")

say_hi()

三、装饰器可以实现的功能
1、注入参数。为函数提供默认参数,生成新的参数等
2、记录函数的行为。可以统计函数调用次数,缓存函数的结果,计算函数调用耗费的时间
3、预处理和后处理
4、修改函数调用时的上下文

如下为统计函数运行时间的装饰器:

import time

def calruntime(f):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = f(*args, **kwargs)
        end = time.time()
        print(f.__name__,end-start)
        return res
    return wrapper

@calruntime
def add(a,b):
    time.sleep(1)
    return a+b

print(add(1,5))

四、使用装饰器后函数属性的变化

@calruntime
def add(a,b):
    """calculate the sum of two numbers"""
    time.sleep(1)
    return a+b


def mul(a,b):
    """calculate the mul of two numbers"""
    return a+b
    
print(add.__name__)
print(add.__doc__)

print(mul.__name__)
print(mul.__doc__)

mul
calculate the mul of two numbers
wrapper
None
可见使用装饰器后,无法获取函数的属性,比如__name__和__doc__等。
可以通过标准库的functools模块中的wraps装饰器可以解决,wraps的作用是复制函数属性到被装饰的函数中。

def calruntime(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        start = time.time()
        res = f(*args, **kwargs)
        end = time.time()
        print(f.__name__,end-start)
        return res
    return wrapper

@calruntime
def add(a,b):
    """calculate the sum of two numbers"""
    time.sleep(1)
    return a+b

def mul(a,b):
    """calculate the mul of two numbers"""
    return a+b

print(mul.__name__)
print(mul.__doc__)

print(add.__name__)
print(add.__doc__)

mul
calculate the mul of two numbers
add
calculate the sum of two numbers

五、使用inspect获取函数参数

import time
import functools
import inspect

def calruntime(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        func_args = inspect.getcallargs(f, *args, **kwargs)
        print(func_args)
        start = time.time()
        res = f(*args, **kwargs)
        end = time.time()
        print(f.__name__,end-start)
        return res
    return wrapper

@calruntime
def add(a,b):
    """calculate the sum of two numbers"""
    time.sleep(1)
    return a+b

六、给装饰器传递参数

import time
import functools
import inspect

def times(n):
    def calruntime(f):
        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            func_args = inspect.getcallargs(f, *args, **kwargs)
            print(func_args)
            start = time.time()
            for i in range(n):
                f(*args, **kwargs)
            end = time.time()
            print(f.__name__,end-start)
        return wrapper
    return calruntime
@times(5)
def sandswitch(name):
    time.sleep(1)
    print(name)

sandswitch("hello")

七、装饰器的缺点
虽然装饰器功能强大,但不能乱用。一方面因为装饰器语法比较复杂,另一方面,装饰器具有速度慢和难以调试的缺点。因此装饰器需要特别注意应用场景,使用得当才能发挥它的优势。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值