day4之装饰器进阶、生成器迭代器

装饰器进阶

带参数的装饰器

# 某一种情况
# 500个函数加装饰器, 加完后不想再加这个装饰器, 再过一个季度,又想加上去
# 你可以设计你的装饰器,来确认是否执行
# 第一种情况
# 想要500个函数执行装饰器的话,可以定义个变量flag
# Flag = False
# import time
# def outer(flag):
#     def timer(f):
#         def inner(*args, **kwargs):
#             if flag == True: #如果flag==ture,装饰器函数执行
#                 start_time = time.time()
#                 ret = f(*args, **kwargs)
#                 end_time = time.time()
#                 print(end_time - start_time)
#             else:   #否则不执行
#                 ret = f(*args, **kwargs)
#             return ret
#         return inner
#     return timer
#
# @outer(Flag) #最核心的地方,先执行outer(flag)== timer, 相当于@timer
# def func(a, b):
#     time.sleep(0.1)
#     print(666)
# func(1, 2)
带参数的装饰器

多装饰器装饰同一个函数

# 第二种情况,多装饰器装饰一个函数
# 装饰器 登录 记录日志
# login_info = {'alex': False}

# def wrapper1(func):   #3 func = f
#     def inner1():
#         print('wrapper1 ,before func')  #14
#         func()  #15 f()
#         print('wrapper1 ,after func')   #17
#     return inner1   #4 返回inner1
#
# def wrapper2(func):  #7 func = inner1,
#     def inner2():   #11
#         print('wrapper2 ,before func')  #12
#         func()   #13 inner1()
#         print('wrapper2 ,after func')   #18
#     return inner2 #8 return inner2
#
# @wrapper2 #6 f = wrapper2(f'-->inner1') = wrapper1(inner1) 9 f = inner2
# @wrapper1 #2 f = wrapper1(f)  5 f = inner1
# def f(): #1
#     print('in f')   #16
#
# f()     #10 inner2()
多装饰器装饰同一函数

生成器和迭代器

迭代器

python中的for循环

for i in [1,2,3,4]:  
    print(i)

换一种情况

for i in 1234:
    print(i)
TypeError: 'int' object is not iterable

说int类型不是一个iterable

迭代和可迭代协议

字符串、列表、元组、字典、集合都可以被for循环,说明他们都是可迭代的

可以将某个数据集内的数据“一个挨着一个的取出来”,就叫做迭代

可以被迭代要满足的要求就叫做可迭代协议。可迭代协议的定义非常简单,就是内部实现了__iter__方法。

想要可迭代的内部必须有个__iter__方法

扫描二维码关注公众号,回复: 78057 查看本文章

__iter__ 都做了些什么

print('asdasd'.__iter__())
结果:
# <str_iterator object at 0x00000000021E7320>

通过 __iter__ 方法,我们得到了个iterator对象

iterator 叫做迭代器

# print(dir([1, 2, 3, 4].__iter__))
'''
['__call__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__objclass__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__text_signature__']
'''

迭代器协议

内部含有__iter__和__next__方法的都是迭代器

迭代器内部多了个__next__() 方法

l1_iter = [1, 2, 3, 4].__iter__()
print(l1_iter.__next__())
print(l1_iter.__next__())
print(l1_iter.__next__())
print(l1_iter.__next__())
print(l1_iter.__next__())   #StopIteration
#
结果:
StopIteration
1
2
3
4
__next__

我们一直取next取到迭代器里已经没有元素了,就会抛出一个异常StopIteration,告诉我们,列表中已经没有有效的元素了。

这个时候,我们就要使用异常处理机制来把这个异常处理掉

l = [1,2,3,4]
l_iter = l.__iter__()
while True:
    try:
        item = l_iter.__next__()
        print(item)
    except StopIteration:
        break

迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。

range()

# py2 range,不管range多少, 会生成一个list,这个列表存储所以值
# py3 range, 不管range多少,都不会生成实际的任何一个值,只有在找他要值的时候才会没要一个值,生成一个值
print('__next__' in dir(range(12)))  #查看'__next__'是不是在range()方法执行之后内部是否有__next__
print('__iter__' in dir(range(12)))  #查看'__next__'是不是在range()方法执行之后内部是否有__next__

from collections import Iterator
print(isinstance(range(100000000),Iterator))  #验证range执行之后得到的结果不是一个迭代器
# __next__() ==  next()
# __iter__() == iter()
#
# l = [1, 2, 3]
# while True:
#     l1 = iter(l)
#     print(next(l1))
# while 每次循环都会生成一个新的迭代器对象,所以每次取值都是第一值
# 迭代器节省内存的例子
'''
f = open()
for i in f:
    print i
g.close()
'''
# 列表, 字典, 元组, 字符串, 集合,range,文件句柄
# 可要是可迭代的对象,就可以通过iter方法转换为迭代器

生成器

# 自己写的迭代器,就是一个生成器
# 两种方法写生成器(迭代器)的机制:生成器函数, 生成器表达式

Python中提供的生成器:

1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行

2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列

生成器Generator:

  本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)

  特点:惰性运算,开发者自定义

生成器函数

 一个包含yield关键字的函数就是生成器函数,yield可以为我们从函数中返回值,但是yield又不同于return

调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,

就能推动函数的执行,获取新的返回值。直到函数执行结束。

# def func():
#     print(666)
#     yield 1
# print(func())  # --><generator object func at 0x0000000002670C50>
# 生成器函数的调用不会触发代码的执行,而是返回一个生成器(迭代器)

# g = func()
# next(g)   #-->666

# def func():
#     print(666)
#     yield 1
#     print(777)
#     yield 2
#
# g = func()
# print('--', next(g))
# print('--', next(g))
# yield记录当前所在的位置, 等待下次next来触发函数的状态
# 想要生成器函数执行,需要用next
初始生成器函数

更多应用

# 使用生成器监听文件输入的例子
# import time
# def listen_file():
#     with open('11.txt') as f:
#         while True:
#             line = f.readline()  #这个位置不用fot循环的原因,是因为for知道文件什么时候结束,readline不知道文件结束,会一直去读
#             if line.strip():
#                 yield line
#             time.sleep(0.1)
#
# g = listen_file()
# for line in g:
#     print(line)
生成器监听文件文件输入的例子

send关键字

# 在执行next的过程中,传递一个参数给生成器函数的内部
# 向生成器中传递值,有一个激活的过程,第一次必须要用next触发这个是生成器

# def func():
#     print(66)
#     ret1 = yield 1
#     print(77, 'ret1:', ret1)
#     ret2 = yield 2
#     print(88, 'ret2:', ret2)
#     ret3 = yield 3
# g = func()
# ret = next(g)
# print(ret)
# g.send('alex')
# g.send('jin-xin')
# g.send('jign')   #--》StopIteration  
# def average():
#     sum_money = 0
#     day = 0
#     avg = 0
#     while True:
#         money = yield avg
#         sum_money += money
#         day += 1
#         avg = sum_money / day
#
# g = average()
# next(g)
# print(g.send(200))
# print(g.send(300))
# print(g.send(500))
计算移动平均值的例子
# 预激生成器
# 计算移动平均值
# def average():
#     sum_money = 0
#     day = 0
#     avg = 0
#     while True:
#         money = yield avg
#         sum_money += money
#         day += 1
#         avg = sum_money / day
#
# g = average()
# next(g)   # 预激活
# print(g.send(200))
# print(g.send(300))
# print(g.send(500))


# 目前有个需求省略与激活这一步

# def init(func1):
#     def inner(*args, **kwargs):
#         ret = func1(*args, **kwargs)
#         next(ret)
#         return ret
#     return inner
#
# @init
# def average():
#     sum_money = 0
#     day = 0
#     avg = 0
#     while True:
#         money = yield avg
#         sum_money += money
#         day += 1
#         avg = sum_money / day
#
# g = average()
# print(g.send(200))
# print(g.send(300))
# print(g.send(500))
通过装饰器实现预激生成器

列表推导式和生成器表达式

# yield from py3新加的为了简化代码
# 如何从生成器中取值
'''
def generator_func():
    for i in range(5):
        yield i
    for j in 'hello':
        yield j


'''

# 等价于
'''
yield from range(5)  == for i in range(5):yield i
yield from 'hello' == for j in 'ghello':yield j
'''
def generator_func():
    yield from range(5)
    yield from 'hello'
yield from 简化代码
# g = generator_func()
# 第一种取值方式 next
# 随时都可以停止,最后一次会报错
# print(next(g))

# 第二种方式 for循环
# 从头到尾遍历一次,不遇到break,return 不会停止
# for i in g:
#     print(i)

# 第三种,list ,tuple
# 该方式会一次性取出所有值,该方法不太好
# print(list(g))
# print(tuple(g))
生成器的取值方式
# 踩到的坑
# g1 = generator_func()
# g2 = generator_func()
# print(g1, g2)
'''
<generator object generator_func at 0x0000000002140C50> <generator object generator_func at 0x0000000002140D00>
可以看出每次生成一个新的生成器对象,所以直接用generator_func()取值的话会有问题

列表推导式

# 找到name中含有两个e的名字
# names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
#          ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
# print([ n for n_list in names for n in n_list if n.count('e') == 2])

生成器表达式

# g = (i for i in range(30) if i %3 ==0)
# print(g)
# --><generator object <genexpr> at 0x0000000002670C50>

总结:

1.把列表解析的[]换成()得到的就是生成器表达式

2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存

 生成器相关的面试题

# 关于生成器的面试题
# def add(n, i):
#     return n + i
# def test():
#     for i in range(4):
#         yield i
#
# g = test()
# for n in [1, 10]:
#     g = (add(n, i) for i in g)
'''
# n=1
# g = (add(10, i) for i in for i in [0,1,2,3])
# n = 10
# g = (add(10, i) for i in add(10, i) for i in [0,1,2,3])
# g = (add(10, i) for i in [10,11,12,13])
# g = (20,21,22,23)
'''
# print(list(g))



def add(n, i):
    return n + i
def test():
    for i in range(4):
        yield i


g = test()
for n in [1,2,10]:
    g = (add(n, i) for i in g)
'''
n=1  g = (add(1, i) for i in test())
n=2  g = (add(2, i) for i in (add(2, i) for i in test()))
n=10 g = (add(10, i) for i in (add(10, i) for i in (add(10, (0,1,2,3)))) )
n=10 g = (add(10, i) for i in (add(10, (10,11,12,13))) )
n=10 g = (add(10, (20,21,22,23))) )
n=10 g = (30,31,32,33)) )
'''
print(list(g))
面试题1
def demo():
    for i in range(4):
        yield i

g=demo()

g1=(i for i in g)   #已经取完g中的值   
g2=(i for i in g1)  #取的空列表

print(list(g1))
print(list(g2))
'''
结果:
[0, 1, 2, 3]
[]
'''
面试题2
import os

def init(func):
    def wrapper(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return wrapper

@init
def list_files(target):
    while 1:
        dir_to_search=yield
        for top_dir,dir,files in os.walk(dir_to_search):
            for file in files:
                target.send(os.path.join(top_dir,file))
@init
def opener(target):
    while 1:
        file=yield
        fn=open(file)
        target.send((file,fn))
@init
def cat(target):
    while 1:
        file,fn=yield
        for line in fn:
            target.send((file,line))

@init
def grep(pattern,target):
    while 1:
        file,line=yield
        if pattern in line:
            target.send(file)
@init
def printer():
    while 1:
        file=yield
        if file:
            print(file)

g=list_files(opener(cat(grep('python',printer()))))

g.send('/test1')

协程应用:grep -rl /dir

tail&grep
面试题3

小结:

# 一个生成器只能取一次
# 生成器在不找它要值的时候,始终不执行
# 当他执行的时候,要以执行时候的所以变量值为准

猜你喜欢

转载自www.cnblogs.com/zh-lei/p/8954993.html
今日推荐