yiruia的个人博客分享 http://blog.sciencenet.cn/u/yiruia

博文

Day 12 装饰器的进阶

已有 1040 次阅读 2020-3-4 10:16 |系统分类:科研笔记

一、functools.wraps

二、带参数的装饰器

三、多个装饰器装饰同一个函数

四、生成器函数


一、装饰器

(一)原则

开放封闭原则,对修改是封闭的。只能扩展功能

图片.png

(二)作用

不改变原函数调用方式的情况下,在函数的前后添加功能,装饰器的本质是闭包函数。

def wrapper(func):# func是被装饰函数
    def inner(*args,**kwargs):
        print('''被装饰前的操作''')
        ret = func(*args,**kwargs) # 被装饰函数,只是一个中间传话的
        print('''在被装饰后的操作''')
        return ret
    return inner

@wrapper #holiday = wrapper(holiday)
def holiday(day):
    print('全体放假%s天'%day)
    return '好开心'
ret = holiday(3) #实际上执行的是inner
print(ret)
'''
被装饰前的操作
全体放假3天
在被装饰后的操作
好开心
'''
def outer(*args,**kwargs):
    print(args)
    print(*args) #接收是聚合,调用是打散
outer(1,2,3,4)
'''
(1, 2, 3, 4)
1 2 3 4
'''
def outer(*args,**kwargs):
    print(args)
    print(*args)
    def inner(*args):
        print('inner:',args)
    inner(*args)
outer(1,2,3,4)

'''
(1, 2, 3, 4)
1 2 3 4
inner: (1, 2, 3, 4)
'''
def wahaha():
    '''
    一个打印哇哈哈的函数
    :return:
    '''
    print('哇哈哈')
print(wahaha.__name__) # wahaha,输出字符串格式的函数名
print(wahaha.__doc__) # 打印函数注释
def wrapper(func):# func是被装饰函数
    def inner(*args,**kwargs):
        print('''被装饰前的操作''')
        ret = func(*args,**kwargs) # 被装饰函数,只是一个中间传话的
        print('''在被装饰后的操作''')
        return ret
    return inner

@wrapper #holiday = wrapper(holiday)
def holiday(day):
    print('全体放假%s天'%day)
    return '好开心'
print(holiday.__name__) #输出结果为inner,holiday的内存地址已经传给inner了,全局中没有holiday了
# ret = holiday(3) #实际上执行的是inner
print(ret)

二、带参数的装饰器

我们在使用 装饰器 的过程中,难免会损失一些原本的功能信息。
也就是说,原函数的属性失效了。
如果想要保留原函数的属性,就可以用到functools.wraps了。

1、functools.wraps 则可以将原函数对象的指定属性复制给包装函数对象, 默认有 modulenamedoc,或者通过参数选择。代码如下:

from functools import wraps
def wrapper(func):# func是被装饰函数
    @wraps(func) #带参数的装饰器
    def inner(*args,**kwargs):
        print('''被装饰前的操作''')
        ret = func(*args,**kwargs) # 被装饰函数,只是一个中间传话的
        print('''在被装饰后的操作''')
        return ret
    return inner

@wrapper #holiday = wrapper(holiday)
def holiday(day):
    '''
    这是一个放假通知
    :param day:
    :return:
    '''
    print('全体放假%s天'%day)
    return '好开心'
print(holiday.__name__) #输出结果为holiday
print(holiday.__doc__)
ret = holiday(3) #实际上执行的是inner
print(ret)

'''
holiday

    这是一个放假通知
    :param day:
    :return:

被装饰前的操作
全体放假3天
在被装饰后的操作
好开心
'''

代码执行过程

图片.png


2、带参数装饰器的应用

需求:为了季度考核有500个函数需要执行,现在不需要了,但是以后可能还是会用到,如何在不改变原函数的基础上实现这个功能。若需要执行的函数过多,不需要执行此功能时,一个一个更改功能可能太麻烦,可设置一个全局变量对函数进行改进。

(1)未改进前的函数

import time
def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        ret = func(*args,**kwargs)
        end = time.time()
        print(end-start)
        return ret
    return inner

@timer
def wahaha():
    time.sleep(0.1)
    print('wahaha')

@timer
def erguotou():
    time.sleep(0.1)
    print('erguotou')

wahaha()
erguotou()

'''
wahaha
0.1007697582244873
erguotou
0.10069155693054199
'''

(2)改进的函数

import time
FLAGE = True #当FLAGE = True执行装饰器,当FLAGE=False不执行,与之前的函数相比只是增加了一层装饰器
def timer_out(flag):
    def timer(func):
        def inner(*args,**kwargs):
            if flag:
                start = time.time()
                ret = func(*args,**kwargs)
                end = time.time()
                print(end-start)
                return ret
            else:
                ret = func(*args, **kwargs)
                return ret
        return inner
    return timer # 谁调用它,返回给谁,返回给@timer_out()中的timer_out
# timer = timer_out(FLAGE)
# @timer(FLAGE)
@timer_out(FLAGE) #先调用timer_out,传参数FLAGE,接收timer_out参数的返回值timer,加上@符号就是@timer,和之前的一样
def wahaha():
    time.sleep(0.1)
    print('wahaha')

@timer_out(FLAGE)
def erguotou():
    time.sleep(0.1)
    print('erguotou')

wahaha()
erguotou()

三、多个装饰器装饰同一个函数

(1)双层装饰器

def wrapper1(func): #func->f
    def inner1():
        print('wrapper1,before func')
        func() #f()
        print('wrapper1,after func')
    return inner1
def wrapper2(func): # func->inner1
    def inner2():
        print('wrapper2,before func')
        func() # inner1()
        print('wrapper2,after func')
    return inner2
@wrapper2 # f=wrapper2(f)->wrapper2(inner1)=inner2
@wrapper1 # f=wrapper1(f)=inner1
def f():
    print('in f')
f() # inner2

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

图片.png

函数的执行过程

图片.png


(2)三层装饰器

def wrapper1(func):
    def inner1():
        print('wrapper1,before func')
        func()
        print('wrapper1,after func')
    return inner1
def wrapper2(func):
    def inner2():
        print('wrapper2,before func')
        func() # inner1()
        print('wrapper2,after func')
    return inner2
def wrapper3(func):
    def inner3():
        print('wrapper3,before func')
        func() # inner1()
        print('wrapper3,after func')
    return inner3
@wrapper3
@wrapper2
@wrapper1
def f():
    print('in f')
f()
'''
wrapper3,before func
wrapper2,before func
wrapper1,before func
in f
wrapper1,after func
wrapper2,after func
wrapper3,after func
'''

def wrapper1(func):
    def inner1():
        print('wrapper1,before func')
        ret = func()
        print('wrapper1,after func')
        return ret
    return inner1
def wrapper2(func):
    def inner2():
        print('wrapper2,before func')
        ret=func()
        print('wrapper2,after func')
        return ret
    return inner2
def wrapper3(func):
    def inner3():
        print('wrapper3,before func')
        ret=func()
        print('wrapper3,after func')
        return ret
    return inner3
@wrapper3
@wrapper2
@wrapper1
def f():
    print('in f')
    return 'hahaha'
print(f())
'''
wrapper3,before func
wrapper2,before func
wrapper1,before func
in f
wrapper1,after func
wrapper2,after func
wrapper3,after func
hahaha
'''

四、生成器函数

1、只要含有yield关键字的函数都是生成器函数,yeild关键字只能写在函数里,且yeild和return不能同时存在。

生成器函数执行后会得到一个生成器作为返回值。

def generator():
    print(1)
    yield 'a'
# 生成器函数:执行之后会得到一个生成器作为返回值
ret = generator()
print(ret)

'''
<generator object generator at 0x0000021859F797C8>

'''
def generator():
    print(1)
    yield 'a'
# 生成器函数:执行之后会得到一个生成器作为返回值
ret = generator()
print(ret)
print(ret.__next__()) # 生成器就是一个迭代器
'''
<generator object generator at 0x0000021859F797C8>
1
a
'''

2、yield和return的区别

解释一:

就像打电玩一样,你蓄力发大招的时候,如果执行了return,就直接把大招发出去了,蓄力结束,如果执行了yield,就相当于返回一个生成器对象,每调用一次next(),就发一个大招。

解释二:

return是函数返回值,当执行到return,后续的逻辑代码不再执行

yield是创建迭代器,可以用for来遍历,有点事件触发的意思。

def generator():
    print(1)
    yield 'a'
    print(2)
    yield 'b'
g = generator() # g为生成器,generator()为生成器函数
ret = g.__next__()
print(ret)
'''
1
a
'''
def generator():
    print(1)
    yield 'a'
    print(2)
    yield 'b'
    yield 'c'
g = generator() # g为生成器,generator()为生成器函数
ret = g.__next__()
print(ret)
ret = g.__next__()
print(ret)
ret = g.__next__()
print(ret)
'''
1
a
2
b
c
'''
def generator():
    print(1)
    yield 'a'
    print(2)
    yield 'b'
    yield 'c'
g = generator() # g为生成器,generator()为生成器函数
for i in g:
    print(i)
'''
1
a
2
b
c
'''
#哇哈哈%i
def wahaha():
    for i in range(20000):
        yield '哇哈哈%s'%i
g = wahaha()
count = 0
for i in g:
    count += 1
    print(i)
    if count>5:#从生成的函数中取5个值
        break
print('****', g.__next__()) # 如果需要的话,还可以接着再取
'''
哇哈哈0
哇哈哈1
哇哈哈2
哇哈哈3
哇哈哈4
哇哈哈5
**** 哇哈哈6
'''

列表是可迭代的,但不是迭代器

l = [1,2,3,4]
for i in l:
    print(i)
    if i == 2:
        break
for i in l:
    print(i) #循环结束后再次执行函数,从头开始
def wahaha():
    for i in range(20000):
        yield '哇哈哈%s'%i
g = wahaha() #g,g1相当于两个迭代器,各走各的。
g1 = wahaha()
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g1.__next__())
'''
哇哈哈0
哇哈哈1
哇哈哈2
哇哈哈0
'''
def tail(filename):
    f = open('d:\\huangshurui.txt',encoding='utf-8')
    while True:
        line = f.readline()
        if line:
            print('****',line.strip('\r\n'))
tail('huangshurui.txt')
'''
**** 22111
**** 月儿
**** xinger
**** 月儿
**** 星儿
**** nihao
**** 噢噢噢噢 哦哦
'''

针对以上函数,我想实现在每行前面加上***后打印,除此之外还想在在每行加上。。。后打印,解决此问题,可采用生成器函数

def tail(filename):
    f = open('d:\\huangshurui.txt',encoding='utf-8')
    while True:
        line = f.readline()
        if line:
            yield line.strip('\r\n')

g = tail('huangshurui.txt')
for i in g:
    if '月儿' in i: #监听过滤的效果
        print('****',i)




https://blog.sciencenet.cn/blog-3405644-1221699.html

上一篇:Day11 Python装饰器的初成
下一篇:Day13 作业
收藏 IP: 223.91.59.*| 热度|

0

该博文允许注册用户评论 请点击登录 评论 (0 个评论)

数据加载中...

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-5-20 01:01

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部