python函数装饰器

一、装饰器
在不改变原函数的基础上,给函数增加功能,把一个函数当作参数,返回一个替代版的函数
原则:1、不能修改被装饰 函数的源代码;
2、不能修改被装饰的函数的调用方式
本质上:一个返回函数的函数
二、装饰器的举例

1.要实现在我调用的函数得到的输出结果之前和之后打印两行“*“号

# _*_ coding:utf-8 _*_
def func1():
    print 'have a good time'

def func2():
    print 'good luck'

def outer(func):
    def inner():
        print '**********'
        func()
        print '##########'
    return inner
# outer(func1)
# outer(func2)
func1 = outer(func1)
func1()
"""结果如下"""
# **********
# have a good time
# **********

上面的outer函数就是一个装饰器,func为形参接受一个函数,inner为返回的函数。即经过装饰的函数。

2.模拟银行ATM显示:针对不同的节日ATM机的显示应该是不同的,即可以改变;但是其取钱,存钱的操作是不能改变的,故我们可以写一个装饰器来装扮ATM机的显示。

# _*_ coding:utf-8 _*_
def desc(func):
    def inner():
        print '中秋快乐'
        func()
        print 'python is good'
    return inner

@desc  # 语法糖 要使用装饰器的时候,直接在函数的上面加上@desc
def func1():
    print '欢迎使用...'

@desc
def func2():
    print '取钱...'

@desc
def func3():
    print '存钱...'

# @desc  ==  func1 = desc(func1)
func1()
func2()
func3()
"""结果如下"""
# 中秋快乐
# 欢迎使用...
# 广告1...
# 中秋快乐
# 取钱...
# 广告1...
# 中秋快乐
# 存钱...
# 广告1...

语法糖:@ + 装饰器函数名 (@desc)
使用:若要使用装饰器来装饰一个函数,只需要在函数的开头加上@+装饰器函数名即可。
实质:@ + 装饰器函数名 == 被装饰函数 = desc(被装饰函数)
例2中如若现在是春节,广告也换为了广告2,我们只需要改装饰器的内容即可,如下代码所示。

def desc(func):
    def inner():
        print '新春快乐'
        func()
        print '广告2...'
    return inner

三、高级通用装饰器

1.计算一个函数执行的时间

# _*_ coding:utf-8 _*_
import time
import random
import string
import functools
# 列表生成式,通过使用导入的random库和string库的方法,来生成有1000个以大写或者小写字母为元素组成的列表
li = [random.choice(string.ascii_letters) for i in range(1000)]
def timeit(fun):
    """计算函数执行时间的装饰器函数"""
    @functools.wraps(fun)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        res = fun(*args, **kwargs)
        end_time = time.time()
        print '运行时间为:%.6f' % (end_time - start_time)
        return res
    return wrapper

@timeit
def con_add():
    s = ''
    for i in li:
        s += (i + ',')

@timeit
def join_add():
    ','.join(li)

@timeit
def fun_list(n):
    [2 * i for i in range(n)]

@timeit
def fun_map(n):
    list(map(lambda x: x * 2, range(n)))

print con_add()
join_add()
fun_list(50000)
fun_map(50000)
"""结果如下"""
# 运行时间为:0.000231
# 运行时间为:0.000023
# 运行时间为:0.006882
# 运行时间为:0.012010

2.打印日志信息

# _*_ coding:utf-8 _*_
import functools
import time

def add_log(fun):
    @functools.wraps(fun)
    def inner(*args, **kwargs):
        start_time = time.time()
        res = fun(*args, **kwargs)
        end_time = time.time()
        print '日志时间:%s 函数名:%s 运行时间:%.6f 运行结果:%d' \
              % (time.ctime(), fun.__name__, end_time - start_time, res)
        return res
    return inner

@add_log
def add(x, y):
    time.sleep(1)
    return x + y

add(3, 4)
"""结果如下"""
# 日志时间:Thu Sep  6 10:28:11 2018 函数名:add 运行时间:1.000220 运行结果:7

补充:函数的属性
__name__ 属性:用来记录函数的名称
__doc__ 属性:用来记录函数的文档字符串

3.用户验证登陆
需求1:用户登陆验证的装饰器is_login
1).如果用户登陆成功,则执行被装饰的函数
2).如果用户登陆不成功,则执行登陆函数
需求2:判断登陆用户是否为管理员is_admin
1).是,执行装饰函数
2).不是,则报错

# _*_ coding:utf-8 _*_
import functools
login_users = ['admin', 'root']

def is_admin(fun):
    @functools.wraps(fun)
    def wrapper(*args, **kwargs):
        if kwargs.get('name') == 'admin':
            res = fun(*args, **kwargs)
            return res
        else:
            return 'Error,没有权限'

    return wrapper

def is_login(fun):
    @functools.wraps(fun)
    def wrapper(*args, **kwargs):
        if kwargs.get('name') in login_users:
            res = fun(*args, **kwargs)
            return res
        else:
            res = login()
            return res

    return wrapper

@is_login
@is_admin
def writeBlog(name):
    return '编写博客'

def login():
    return '登陆。。。'

print writeBlog(name='root')
"""结果如下"""
# Error,没有权限

print writeBlog(name='admin')
"""结果如下"""
# 编写博客

小结:
一个函数的装饰器可以有多个,如要使用多个装饰器,在函数的定义之前加上对应装饰器的语法糖即可。
多个装饰器执行顺序的说明:简言之从下向上调用,从上到下执行(详细解读可参考博文《探究多个装饰器执行顺序》)

4.判断函数所跟参数是否合法

# _*_ coding:utf-8 _*_
import functools

def select_types(*kind):
    def required_types(fun):
        @functools.wraps(fun)
        def wrapper(*args, **kwargs):
            for i in args:
                if isinstance(i, kind):
                    pass
                else:
                    print 'TypeError:参数必须为', kind
                    break
            else:
                res = fun(*args, **kwargs)
                return res

        return wrapper

    return required_types

@select_types(int, float)
def add(a, b):
    return a + b

@select_types(list)
def li(*args):
    return args

@select_types(str, int)
def li1(a, b):
    return a, b

print add(12, 1.2)
print add(12, '1')
print li([1, 2, 3])
print li1('str', 1)
"""结果如下"""
# 参数合法
# 13.2
# 参数不合法:参数必须为 (<type 'int'>, <type 'float'>)
# None
# 参数合法
# ([1, 2, 3],)
# 参数合法
# ('str', 1)

总结:
函数装饰器经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。
有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用,提高了工作的效率。

猜你喜欢

转载自blog.csdn.net/oikinkl/article/details/82455086