zoukankan      html  css  js  c++  java
  • 装饰器进阶、生成器迭代器

    装饰器进阶

    带参数的装饰器

    # 某一种情况
    # 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__方法

    __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

    小结:

    # 一个生成器只能取一次
    # 生成器在不找它要值的时候,始终不执行
    # 当他执行的时候,要以执行时候的所以变量值为准
  • 相关阅读:
    C# 中的委托和事件
    css样式大全(整理版)
    (转)VS2010 快捷键
    委托小例子
    .NET中Cache的使用
    ObjectiveC面向对象编程继承
    ObjectiveC简介
    ObjectiveC面向对象编程实例化对象、构造函数
    MSSql的多表关联的update语句
    ObjectC 与 Java 区别
  • 原文地址:https://www.cnblogs.com/zh-lei/p/8954993.html
Copyright © 2011-2022 走看看