铁乐学Python_day12_装饰器

【函数的有用信息】

例:
def login(user, pwd):
    '''
    功能:登录调用
    参数:分别有user和pwd,作用分别是用户和密码;
    return: 返回值是登录成功与否(True,False)
    '''
    print(user, pwd, "欢迎登录")

print(login.__name__) #查看函数名字
print(login.__doc__)  #查看函数注释

login

    功能:登录调用
    参数:分别有user和pwd,作用分别是用户和密码;
    return: 返回值是登录成功与否(True,False)

函数名.__name__ 这是查看函数名字的方法
函数名.__doc__ 这是查看函数注释的方法

上面的两个方法在做运维审计日志时很有用,配合装饰器使用可以记录下不少信息。
不过要是用在被装饰器装饰过的函数时显示的会变成是装饰器的函数名和注释,
所以需要用到wraps模块去实现显示的名字和注释仍是被装饰的原函数。

例:

from functools import wraps
# 函数工具库的warps模块来实现装饰器函数名字仍是被装饰的原函数名字

def deco(func):
    @wraps(func) #加在最内层函数正上方
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@deco
def index():
    '''首页的注释'''
    print('from index')

print(index.__doc__)
print(index.__name__)

首页的注释
index

【装饰器Decorator】

装饰器本质上就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。
装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。

装饰器的语法糖:@紧接着装饰器的函数名,放置在需被装饰的函数上方。
因为@的形象很像一个甜甜的一个一个圈的棒棒糖,所以又被人们爱称为语法糖。
相对直接生硬的将要被装饰的函数名赋值成装饰器的函数名这种方式来说灵活和优雅得多,
就算要改动也不致于太麻烦。

例:下面是一个在调用函数时先进行打印格林威治时间的Decorator:

import time  # 引入时间模块

def wrapper(func):
    '''
    此装饰器主要用于测试被装饰函数的工作时间
    '''
    def inner(*args, **kwargs):
        start_time = time.time()
        ret = func(*args, **kwargs)
        time.sleep(0.2322) # 模拟函数执行后经过的时间
          end_time = time.time()
        sec = end_time - start_time
        print('此程序执行时间为%s' % round(sec, 5))
        return ret # 相当于是返回func()
    return inner

@wrapper # 语法糖装饰上后,相当于是将下方的 hello = wrapper(hello)
def hello():
    print('hero say hello')

hello() 
# 相当于是执行wrapper(hello)(),就相当于是执行wrapper里层的inner函数体。
# 所以会执行额外添加的功能,同时函数也正常调用了。

# 执行效果如下:
hero say hello
此程序执行时间为0.23301


上面例子是嵌套的装饰器,它比较简单。
因为我们在内层的inner函数中有设置动态参数(*args和**kwargs)
被装饰的函数有设置参数的时候,也可以正常传递,如:
@wrapper # 语法糖装饰上后,相当于是将下方的 hello = wrapper(hello)
def hello(name):
    print('hero say hello %s' % name)

hello('tiele')
# 相当于是执行wrapper(hello)(),就相当于是执行wrapper里层的inner函数体。
# 所以会执行额外添加的功能,同时函数也正常调用了。

返回:
hero say hello tiele
此程序执行时间为0.23301


但有一点要注意的
经过decorator装饰之后的函数,它们的__name__已经从原来的'函数名'变成了'inner':

print(hello.__name__)

hero say hello tiele
此程序执行时间为0.23301
inner

因为直接用查看函数名字与注释的方法去查看经过被装饰器装饰了的函数会得出的是错误的信息(返回的是装饰器的名字与注释),这不符合我们的需求,(虽然直接在inner函数体内打印func.__name__可以得出原函数名,但在全局环境下使用.__name__查看还是会出错)所以还得用上python内置的functools.wraps去支持全局查看函数名时能看到正确的原函数名:

import time  # 引入时间模块
import functools # 引入函数工具模块

def wrapper(func):
    '''
    此装饰器主要用于测试被装饰函数的工作时间
    '''
    # 加在最内层函数的正上方,因为要的是最内函数的名字
    @functools.wraps(func)
    def inner(*args, **kwargs):
        start_time = time.time()
        ret = func(*args, **kwargs)
        time.sleep(0.2322) # 模拟函数执行后经过的时间
        end_time = time.time()
        sec = end_time - start_time
        print('此程序执行时间为%s' % round(sec, 5))
        return ret # 相当于是返回func()
    return inner

@wrapper # 语法糖装饰上后,相当于是将下方的 hello = wrapper(hello)
def hello(name):
    print('hero say hello %s' % name)

hello('tiele')
# 相当于是执行wrapper(hello)(),就相当于是执行wrapper里层的inner函数体。
# 所以会执行额外添加的功能,同时函数也正常调用了。

print('函数名为'+ hello.__name__)

hero say hello tiele
此程序执行时间为0.23301
函数名为hello

如上例,看起来像是在inner函数上方也装了一个语法糖装饰器一样。

【带参数的装饰器】

函数中带参数,我们是实现了。可是,有些情况需要装饰器本身也带上参数以便控制流程和装饰器开关的,就很有必要再嵌套一层外面的函数去实现了。

例:(最外层这次参数内是动态参数,而不是接受被装饰的函数名做参数了)

from functools import wraps
# 函数工具库的warps模块来实现装饰器函数名字仍是被装饰的原函数名字
import time
# 引进时间模块

def timer(*args, **kwargs):
    def wrapper(f):
        @wraps(f)
        # 加在最内层函数的正上方,因为要的是最内函数的名字
        def inner(*args, **kwargs):
            '''
            此功能用于测试函数起始时间与结束时间
            :param args:
            :param kwargs:
            :return:
            '''
            if flag:
                StartTime = time.time()
                ret = f(*args, **kwargs)
                time.sleep(0.3)
                EndTime = time.time()
                sec = EndTime - StartTime
                print('此程序执行效率%f' % sec)
            else:
                ret = f(*args, **kwargs)
            return ret
        return inner
    return wrapper

flag = True
@timer()
def hello_word():
    '''
    向世界say hello
    :return: None
    '''
    print('hello,word.hello,hero.')

hello_word()
print(hello_word.__name__)
print(hello_word.__doc__)

hello,word.hello,hero.
此程序执行效率0.300017
hello_word

    向世界say hello
    :return: None



flag为False,装饰器的计算时间功能便不会执行。

def timer(flag):
    # 带flag参数,可以控制装饰器的开关
    def wrapper(f):
        @wraps(f)
        # 加在最内层函数的正上方,因为要的是最内函数的名字
        def inner(*args, **kwargs):
            '''
            此功能用于测试函数起始时间与结束时间
            :param args:
            :param kwargs:
            :return:
            '''
            if flag:
                StartTime = time.time()
                ret = f(*args, **kwargs)
                time.sleep(0.3)
                EndTime = time.time()
                sec = EndTime - StartTime
                print('此程序执行效率%f' % sec)
            else:
                ret = f(*args, **kwargs)
            return ret
        return inner
    return wrapper

@timer(False)
def hello_word():
    '''
    向世界say hello
    :return: None
    '''
    print('hello,word.hello,hero.')

hello_word()

hello,word.hello,hero.

上面例子中,当我们将flag的值改成False,便不会触发装饰器的额外功能。

【开放封闭原则】

1.对扩展是开放的
  为什么要对扩展开放呢?
  任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。
所以必须允许代码扩展、添加新功能。

2.对修改是封闭的
  为什么要对修改封闭呢?
  写的一个函数,很有可能已经交付给其他人使用了,如果这个时候对其进行了修改,很有可能影响其他已经在使用该函数的用户。

装饰器完美的遵循了这个开放封闭原则。

【多个装饰器装饰一个函数】

例:

def wrapper1(func):
    def inner():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner

def wrapper2(func):
    def inner():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner

@wrapper2
@wrapper1
def f():
    print('in f')

f()

效果:

wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func


规律就是先执行完原里函数前面的语句,再执行原函数,再到里函数后面的语句。
如图:

end
2018-4-4

发表评论

电子邮件地址不会被公开。 必填项已用*标注

8 + 4 =