day4_python-之装饰器、迭代器、生成器

一、装饰器

1、为何要用装饰器

#开放封闭原则:对修改封闭,对扩展开放

2、 什么是装饰器

装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。
强调装饰器的原则:1 不修改被装饰对象的源代码 2 不修改被装饰对象的调用方式
装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能
import time


def index():
    time.sleep(3)
    print('welcome to index')


# index()

def wrapper(func):
    start_time = time.time()
    func()
    stop_time = time.time()
    print('run time is %s' % (stop_time - start_time))


wrapper(index)


# ================闭包实现=============================
import time


def index():
    time.sleep(3)
    print('welcome to index')


def timmer():
    func = index  # 这么写严格收到层级限制了,内部的函数无法调取,需要return返回值

    def wrapper():  # 注意这里,func已经定义了等于index,就不需要写默认参数了
        start_time = time.time()
        func()
        stop_time = time.time()
        print('run time is %s' % (stop_time - start_time))

    return wrapper


# wrapper=timmer()       #运行timmer,得到的结果是wrapper,并且把值赋给了wrapper
# wrapper()

index = timmer()  # 如上,既然可以把timmer运行的返回值赋值给warpper,那么同理也可以赋值给index
index()

####这么写没有修改源代码,也没有修改调用方式,也加上的新功能。实现了装饰器
####这么写其实是有问题的,如果又来了一个函数。这种闭包就无法实现了。解决方法如下
# ==============================================

import time


def index():
    time.sleep(3)
    print('welcome to index')


def home():
    time.sleep(2)
    print('welcome to home page')


# index()
def timmer(func):
    # func=index
    def wrapper():  # 注意这里,func已经定义了等于index,就不需要写默认参数了
        start_time = time.time()
        func()
        stop_time = time.time()
        print('run time is %s' % (stop_time - start_time))

    return wrapper


index = timmer(index)  # 给func一个index的值,最后返回的值是wrapper,我可以把它赋值给index,home同理
home = timmer(home)
index()
home()

# =================装饰器=======================




import time



def timmer(func):
    # func=index
    def wrapper():
        start = time.time()
        func()
        stop = time.time()
        print('run time is %s' % (stop - start))

    return wrapper


@timmer  # index=timmer(index)   自上而下运行,代表调用timmer 并且给timmer传值为index
def index():
    time.sleep(3)
    print('welcome to index')


@timmer # home=timmer(home)
def home():
    time.sleep(2)
    print('welcome to home page')

index()
home()



# =================被装饰器是有参函数=======================


import time



def timmer(func):
    # func=index
    def wrapper(name):
        start = time.time()
        func(name)
        stop = time.time()
        print('run time is %s' % (stop - start))

    return wrapper


@timmer  # home=timmer(home)
def home(name):
    time.sleep(2)
    print('welcome %s home ' % (home))


home('egon')


# ===========有参和无参混合用=======================
import time



def timmer(func):
    # func=index
    def wrapper(*args, **kwargs):  # 闭包函数适应有参无参,各种* 传参的方式
        start = time.time()
        func(*args, **kwargs)
        stop = time.time()
        print('run time is %s' % (stop - start))

    return wrapper


@timmer  # index=timmer(index)   自上而下运行,代表调用timmer 并且给timmer传值为index
def index():
    time.sleep(3)
    print('welcome to index')


@timmer  # home=timmer(home)
def home(name):
    time.sleep(2)
    print('welcome %s to home ' % (name))


index()
home('agon')
View Code
import time


def timmer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)

        stop = time.time()
        print('run time is %s' % (stop - start))
        return res

    return wrapper


@timmer  # index=timmer(index)
def index():
    time.sleep(3)
    print('welcome to index')
    return 123


@timmer  # home=timmer(home)
def home(name):
    time.sleep(2)
    print('welcome %s to home page' % name)


# res=index() #res=wrapper()
# print(res)

res1 = home('egon')  # wrapper('egon')
print(res1)
装饰器返回值

3、装饰器的使用

import time
def timmer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res
    return wrapper

@timmer
def foo():
    time.sleep(3)
    print('from foo')
foo()
无参装饰器
def auth(driver='file'):
    def auth2(func):
        def wrapper(*args,**kwargs):
            name=input("user: ")
            pwd=input("pwd: ")

            if driver == 'file':
                if name == 'egon' and pwd == '123':
                    print('login successful')
                    res=func(*args,**kwargs)
                    return res
            elif driver == 'ldap':
                print('ldap')
        return wrapper
    return auth2

@auth(driver='file')
def foo(name):
    print(name)

foo('egon')
有参装饰器

4、装饰器语法

被装饰函数的正上方,单独一行
        @deco1
        @deco2
        @deco3
        def foo():
            pass

        foo=deco1(deco2(deco3(foo)))

5、加认证功能的装饰器

current_user = {'user': None}


def auth(func):
    def wrapper(*args, **kwargs):
        if current_user['user']:
            return func(*args, **kwargs)

        name = input('name: ').strip()
        password = input('password: ').strip()

        with open('db.txt', encoding='utf-8') as f:
            user_dic = eval(f.read())
        if name in user_dic and password == user_dic[name]:
            res = func(*args, **kwargs)
            current_user['user'] = name
            return res
        else:
            print('user or password error')

    return wrapper


@auth  # index=auth(index) index=wrapper
def index():
    print('from index')


index()


@auth
def home(name):
    print('welcome %s' % name)


index()  # wrapper()
home('egon')
无参版本
current_user = {'user': None}


def auth(auth_type='file'):
    def deco(func):
        def wrapper(*args, **kwargs):
            if auth_type == 'file':
                if current_user['user']:
                    return func(*args, **kwargs)
                name = input('name: ').strip()
                password = input('password: ').strip()

                with open('db.txt', encoding='utf-8') as f:
                    user_dic = eval(f.read())
                if name in user_dic and password == user_dic[name]:
                    res = func(*args, **kwargs)
                    current_user['user'] = name
                    return res
                else:
                    print('user or password error')
            elif auth_type == 'mysql':
                print('mysql')

            elif auth_type == 'ldap':
                print('ldap')
            else:
                print('not valid auth_type')

        return wrapper

    return deco


@auth(auth_type='mysql')  # @deco  #index=deco(index)
def index():
    print('from index')


@auth(auth_type='file')
def home(name):
    print('welcome %s' % name)


index()  # wrapper()
home('egon')
有参版本

6、装饰器补充:wraps  

from functools import wraps

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

@deco
def index():
    '''哈哈哈哈'''
    print('from index')

print(index.__doc__)

二、迭代器

1、迭代的概念

#迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值
while True: #只是单纯地重复,因而不是迭代
    print('===>') 
    
l=[1,2,3]
count=0
while count < len(l): #迭代
    print(l[count])
    count+=1

2、为何要有迭代器?什么是可迭代对象?什么是迭代器对象?  

#1、为何要有迭代器?
对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器

#2、什么是可迭代对象?
可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下
'hello'.__iter__
(1,2,3).__iter__
[1,2,3].__iter__
{'a':1}.__iter__
{'a','b'}.__iter__
open('a.txt').__iter__

#3、什么是迭代器对象?
可迭代对象执行obj.__iter__()得到的结果就是迭代器对象
而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象

文件类型是迭代器对象
open('a.txt').__iter__()
open('a.txt').__next__()


#4、注意:
迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象

3、迭代器对象的使用  

dic={'a':1,'b':2,'c':3}
iter_dic=dic.__iter__() #得到迭代器对象,迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身
iter_dic.__iter__() is iter_dic #True

print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
# print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志

#有了迭代器,我们就可以不依赖索引迭代取值了
iter_dic=dic.__iter__()
while 1:
    try:
        k=next(iter_dic)
        print(dic[k])
    except StopIteration:
        break
        
#这么写太丑陋了,需要我们自己捕捉异常,控制next,python这么牛逼,能不能帮我解决呢?能,请看for循环

4、for循环

#基于for循环,我们可以完全不再依赖索引去取值了
dic={'a':1,'b':2,'c':3}
for k in dic:
    print(dic[k])

#for循环的工作原理
#1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic
#2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码
#3: 重复过程2,直到捕捉到异常StopIteration,结束循环

5、迭代器的优缺点

#优点:
  - 提供一种统一的、不依赖于索引的迭代方式
  - 惰性计算,节省内存
#缺点:
  - 无法获取长度(只有在next完毕才知道到底有几个值)
  - 一次性的,只能往后走,不能往前退

三、生成器  

1、什么是生成器  

#只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码

def func():
    print('====>first')
    yield 1
    print('====>second')
    yield 2
    print('====>third')
    yield 3
    print('====>end')

g=func()
print(g) #<generator object func at 0x0000000002184360>

2、生成器就是迭代器

g.__iter__
g.__next__
#2、所以生成器就是迭代器,因此可以这么取值
res=next(g)
print(res)

3、练习

1、自定义函数模拟range(1,7,2)

2、模拟管道,实现功能:tail -f access.log | grep '404'

#题目一:
def my_range(start,stop,step=1):
    while start < stop:
        yield start
        start+=step

#执行函数得到生成器,本质就是迭代器
obj=my_range(1,7,2) #1  3  5
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj)) #StopIteration

#应用于for循环
for i in my_range(1,7,2):
    print(i)

#题目二
import time
def tail(filepath):
    with open(filepath,'rb') as f:
        f.seek(0,2)
        while True:
            line=f.readline()
            if line:
                yield line
            else:
                time.sleep(0.2)

def grep(pattern,lines):
    for line in lines:
        line=line.decode('utf-8')
        if pattern in line:
            yield line

for line in grep('404',tail('access.log')):
    print(line,end='')

#测试
with open('access.log','a',encoding='utf-8') as f:
    f.write('出错啦404
')
View Code

3、tail.py -f access.log | grep 'error'

import time


def tail(filepath):
    with open(filepath, 'r') as f:
        f.seek(0, 2)
        while True:
            line = f.readline()
            if line:
                yield line
            else:
                time.sleep(0.2)


def grep(pattern, lines):
    for line in lines:
        if pattern in line:
            print(line, end='')


grep('error', tail('access.log'))
View Code

4、yield与return的比较?

#相同:都有返回值的功能
#不同:return只能返回一次值,而yield可以返回多次值  

四、协程函数  

#yield关键字的另外一种使用形式:表达式形式的yield
def eater(name):
    print('%s 准备开始吃饭啦' %name)
    food_list=[]
    while True:
        food=yield food_list
        print('%s 吃了 %s' % (name,food))
        food_list.append(food)

g=eater('egon')
g.send(None) #对于表达式形式的yield,在使用时,第一次必须传None,g.send(None)等同于next(g)
g.send('蒸羊羔')
g.send('蒸鹿茸')
g.send('蒸熊掌')
g.send('烧素鸭')
g.close()
g.send('烧素鹅')
g.send('烧鹿尾')
View Code

1、练习

1、编写装饰器,实现初始化协程函数的功能

2、实现功能:grep  -rl  'python'  /etc

#题目一:
def init(func):
    def wrapper(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return wrapper
@init
def eater(name):
    print('%s 准备开始吃饭啦' %name)
    food_list=[]
    while True:
        food=yield food_list
        print('%s 吃了 %s' % (name,food))
        food_list.append(food)

g=eater('egon')
g.send('蒸羊羔')

#题目二:
#注意:target.send(...)在拿到target的返回值后才算执行结束
import os
def init(func):
    def wrapper(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return wrapper

@init
def search(target):
    while True:
        filepath=yield
        g=os.walk(filepath)
        for dirname,_,files in g:
            for file in files:
                abs_path=r'%s\%s' %(dirname,file)
                target.send(abs_path)
@init
def opener(target):
    while True:
        abs_path=yield
        with open(abs_path,'rb') as f:
            target.send((f,abs_path))
@init
def cat(target):
    while True:
        f,abs_path=yield
        for line in f:
            res=target.send((line,abs_path))
            if res:
                break
@init
def grep(pattern,target):
    tag=False
    while True:
        line,abs_path=yield tag
        tag=False
        if pattern.encode('utf-8') in line:
            target.send(abs_path)
            tag=True
@init
def printer():
    while True:
        abs_path=yield
        print(abs_path)


g=search(opener(cat(grep('你好',printer()))))
# g.send(r'E:CMSaaadb')
g=search(opener(cat(grep('python',printer()))))
g.send(r'E:CMSaaadb')
View Code
原文地址:https://www.cnblogs.com/xiechao621/p/8007179.html