铁乐学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

原文地址:https://www.cnblogs.com/tielemao/p/8719122.html

时间: 2024-07-30 17:34:36

铁乐学Python_day12_装饰器的相关文章

铁乐学Python_day12_作业

1.写函数,返回一个扑克牌列表,里面有52项,每一项是一个元组 例如:[('红心',2),('草花',2), -('黑桃','A')] def poker(): suit = ['红心', '梅花', '黑桃', '方片'] count = [i for i in range(2, 11)] king = ['J', 'Q', 'K', 'A'] poker_list = count + king li = [] for key in suit: for value in poker_list:

Day4 - 迭代器&生成器、装饰器、Json & pickle 数据序列化、软件目录结构规范

---恢复内容开始--- 本节内容 迭代器&生成器 装饰器 Json & pickle 数据序列化 软件目录结构规范 作业:ATM项目开发 1.列表生成式,迭代器&生成器 列表生成式 需求:列表a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],要求把列表里的每个值加1 1 a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 2 b = [] 3 for i in a: 4 b.append(i+1) 5 a = b 6 print(a) 普通青

python3 装饰器

看廖雪峰官网的python3装饰器有感 装饰器即将一个函数作为变量在新的函数中调用此函数. 作业: 能否写出一个@log的decorator,使它既支持: @logdef f():     pass 又支持: @log('execute')def f():     pass      例1: import functools import time def log(*args,**kwargs):     # *args 是个元组     if args and isinstance(args,

day14 带函数的装饰器、多个装饰器装饰一个函数

一.带参数的装饰器:------开关 __author__ = 'Administrator' F=True def outer(F): def wap(fun):#gg def inner(*args,**kwargs): if F: print("inner before") ret=fun(*args,**kwargs)#gg() print("inner after") else: ret=fun(*args,**kwargs) return ret ret

Python 装饰器

一.定义 器即函数 装饰即修饰,意指为其他函数添加新功能 装饰器本身可以是任意可调用对象,被装饰的对象本身也可以是任意可调用对象 实现装饰器: 装饰器=高阶函数+函数嵌套+闭包 二.原则: 1 .开放封闭原则:对扩展是开放的,对修改是封闭 2.1 装饰器的遵循的原则:1 不修改被装饰对象的源代码 2 不修改被调用对象的调用方式 三.目的 装饰器的目的是:在遵循1和2原则的前提,为其他新功能函数添加 四.定义位置 @装饰器名,必须写在被装饰对象的正上方,并且是单独一行 import time de

装饰器、生成器、迭代器

装饰器的前奏 装饰器:本质是函数 功能:就是装饰成其他函数  就是为其他函数添加附加功能的 高阶函数+嵌套函数=装饰器 原则:1.不能修改被装饰的函数的源代码 2.不能修改被装饰的函数的调用方式 总结一句话:装饰器对被装饰的函数是完全透明的 实现装饰器的只是储备: 1.函数名即"变量"   将函数体赋值给变量   和内存回收机制一样 2.高阶函数 2.1.把函数名作为实参传递给形参(可返回被修饰函数的地址)(不修改源代码的情况可添加新的功能) 2.2返回值中包含函数地址(不修改函数的调

python 装饰器的用法

为什么要使用装饰器? 在不改变原函数功能的情况,为了添加新的功能 我们可以在函数运行前后给函数添加新的功能 1 def outer(func): 2 #fun()等于原f1函数 3 def inner(): 4 print('ccccc') 5 r=func() 6 print('dddd') 7 return r 8 return inner 9 @outer 10 #@outer代表运行了2个步骤:1.将f1作为参数运行outer函数,2.新函数f1=inner() 11 def f1():

Python装饰器

1.最简单的例子 1 #coding:utf8 2 3 def b(z): 4 print u'装饰器' 5 def func(*args,**kw): 6 print '111' 7 return z(*args,**kw) 8 return func 9 10 @b 11 def a(x): 12 print x 13 print u'函数A' 14 15 a('y')

python3_装饰器_异常处理

装饰器: def auth(func):     def wrapper(name):                     如果函数带参数,要加在这里         user=raw_input("input passwd:").strip()         if user=='test':             print "welcome login"             func(name)              如果函数带参数,要加在这里