• 迭代器和生成器(难点)


    11.1.可迭代对象(iterable):          内部实现了__iter__方法
    可以被迭代满足要求的就叫做可迭代协议。
    iterable:可迭代的------对应的标志
    迭代概念:一个一个取值,就像for循环一样取值
    字符串,列表,元组,集合,字典都是可迭代的
    11.2迭代器协议(iterator):           内部实现了__iter__,__next__方法 
    可以被next()函数调用并且不断返回下一个值的对象称为迭代器
    所有的iterable()均可以通过内置函数iter()转变成iterator
     iterator继承自iterable,iterator包含iter()和next()方法,而iterable仅仅包含iter()
     
    迭代器的优点:如果用了迭代器,节约内存,方便操作
    print(dir([1, 2].__iter__()))   # 查看列表迭代器的所有方法
    print(dir([1, 2]))   #  查看列表的所有方法
    
    print(set(dir([1, 2].__iter__()))-set(dir([1, 2])))    # 对上面两者求差集,去重
    ---{'__setstate__', '__length_hint__', '__next__'}
    iter_l = [1, 2, 3, 4, 5, 6].__iter__()    # 后缀必须有这个
    print(iter_l.__length_hint__())  # 获取迭代器中元素的长度
    ---6
    print(iter_l.__setstate__(4))   # 根据索引指定从哪里开始迭代
    ---None
    print(iter_l.__next__())
    print(iter_l.__next__())
    print(iter_l.__next__())   # 一个一个的取值
    print(next(iter_l))   #  next(iter_l)和iter_l.__next__()方法一样,推荐用next(iter_l)这个
    ---1
    ---2
    ---3
    ---4

     采用for循环模拟可迭代机制:

    li = [1,5,78,1,534,34,51,3,15,6]
    lis = li.__iter__()
    while True:
        try:
            print(next(lis))
        except:        #采用try过滤掉报错
            break
     
    11.3可迭代和迭代器的相同点:都可以用for循环
    11.4可迭代和迭代器的不同点:就是迭代器内部多实现了一个__next__方法
    11.5判断迭代器和可迭代的方法:
    第一种:判断内部是不是实现了__next__方法
    '__iter__' in dir(str)  # 如果__iter__在这个方法里面,就是可迭代的。
    
    s = 'abc'
    print('__iter__' in str(s))
    ---False

     第二种:

        Iterable 判断是不是可迭代对象
        Iterator 判断是不是迭代器
        用法:
    from collections import Iterable
    from collections import Iterator
    s = 'abc'    # 随便给一个字符串
    print(isinstance(s, Iterable))  # isinstance 判断类型的
    print(isinstance(s, Iterator))
    ---True
    ---False

    11.6生成器(Generator )(难点):

    生成器就是一种迭代器,它拥有next方法而且行为与迭代器完全相同,生成器是一种普通函数语法定义的迭代器。任何包含yield语句的函数成为生成器。
    我们定义一个生成器就是定义一个普通函数,但是函数里面有yield关键字,调用生成器函数时会返回一个生成器。
    生成器的好处,就是一下子不会在内存中生成太多的数据
     
    yield 生成器的运行机制:
    当你问生成器要一个数时,生成器会执行,直至出现 yield 语句,生成器把yield 的参数给你,之后生成器就不会往下继续运行。 当你问他要下一个数时,他会从上次的状态开始运行,直至出现yield语句,把参数给你,之后停下。如此反复直至退出函数。
    yield的使用:
    在python中,当你定义一个函数,使用了yield关键字时,这个函数就是一个生成器,它的执行会和其他普通的函数有很多不同,函数返回的是一个对象,而不是你平常所用return语句那样,能得到结果值。如果想取得值,那得调用next()函数
    生成器的本质:就是一个迭代器
    def h():
        print 'To be brave'
        yield 5
    
    h()             # 调用h()之后,print语句并没有执行,因为它有yield表达式

    我们通过next()语句让它执行。next()语句将恢复Generator执行,并直到下一个yield表达式处。比如:

    def h():
        print 'Wen Chuan'
        yield 5
        print 'Fighting!'
    
    c = h()
    print(next(c))  # c. 每当调用一次迭代器的next函数,生成器函数运行到yield之处,
              #  返回yield后面的值且在这个地方暂停,所有的状态都会被保持住,直到下次next函数被调用,或者碰到异常循环退出。
    --- Wen Chuan ---5 # 当我们再次调用next(c)时,会继续执行,直到找到下一个yield表达式,否则报错 def h(): print 'Wen Chuan' yield 5 print 'Fighting!' c = h() c.__next__() # 这里直接调用了next,所以没有打印yield后面的值,和上面对比一下 ---Wen Chuan

    这里多取值会直接报错:

    def func():
        print('aaaaaaaaaaa')
        a = 1
        yield a
        print('bbbbbb')
        yield 12  # 返回第二个值
    
    ret = func()    # 得拿到一个生成器
    print(ret)      # 返回的是一个地址
    print(next(ret))    # 取第一个值,'aaaaaa', 1
    print(next(ret))    # 取第二个值, 'bbb', 12
    print(next(ret))    # 取第三个值,会报错,因为没有yield第三个值
    #注意,这里如果直接连续print(next(func())),一直只会打印第一个yield的内容,注意这里的区别。
    这和上面ret是两个不同的生成器。

    例:创建斐波那契数列:

    def fib(max):
        (a, b) = (1, 1while a < max:
            yield a      # generators return an iterator that returns a stream of values.
           (a, b)  = (b, a+b)
    
    for n in fib(15):
        print(n)
    
    程序运行到yield这行时,就不会继续往下执行。而是返回一个包含当前函数所有参数的状态的iterator对象。
    目的就是为了第二次被调用时,能够访问到函数所有的参数值都是第一次访问时的值,而不是重新赋值。 程序第一次调用时:
    def fib(max): (a, b) = (1, 1while a < max: yield a # 这时a,b值分别为1,1,当然,程序也在执行到这时,返回 (a, b) = (b, a+b) 程序第二次调用时: 从前面可知,第一次调用时,a,b=1,1,那么,我们第二次调用时(其实就是调用第一次返回的iterator对象的next()方法),程序跳到yield语句处, 执行a,b = b, a+b语句,此时值变为:(a,b = 1, (1+1)) => (a,b = 1, 2) 程序继续while循环,当然,再一次碰到了yield a 语句,也是像第一次那样,保存函数所有参数的状态,返回一个包含这些参数状态的iterator对象。 等待第三次的调用....
    # 通过类的方法来实现斐波那契数列
    class Fib:  
        def __init__(self, max):  
            self.max = max  
        def __iter__(self):  
            self.a = 0  
            self.b = 1  
            return self  
        def next(self):  
            fib = self.a  
            if fib > self.max:  
                raise StopIteration  
            self.a, self.b = self.b, self.a + self.b  
            return fib  

    通过yield生成全排列:

    def perm(items, n = None):  
        if n is None:  
            n = len(items)  
        for i in range(len(items)):  
            v = items[i:i+1]  
            if n==1:  
                yield v  
            else:  
                rest = items[:i] + items[i+1:]  
                for p in perm(rest, n-1):  
                    yield v + p  
    def comb(items, n = None):  
        if n is None:  
            n = len(items)  
        else:  
            for i in range(len(items)):  
                v = items[i:i+1]  
                if 1 == n:  
                    yield v  
                else:  
                    rest = items[i+1:]  
                    for c in comb(rest, n-1):  
                        yield v + c 

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

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

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

    3.Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数(sum,sorted,math等),也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和:

    #列表解析
    sum([i for i in range(100000000)])#内存占用大,机器容易卡死    不要运行,千万不要运行
     
    #生成器表达式
    sum(i for i in range(100000000))#几乎不占内存    

    # 使用sorted函数
    >>>sorted(x**2 for x in range(4))
    [0,1,4,9]

    列表推导式链接------ http://www.cnblogs.com/LearningOnline/p/8463906.html

    # 注意:对于生成器

    g = (i for i in range(2))
    print(list(g))
    print(list(g))
    
    ---[0, 1]
    ---[]

    这里为什么第二次再次打印为空了呢?它其实只能被调用一次,第一次调用完第二次调用的时候就没有了。生成器,生成器并不会二次生成,一次使用完就不会再有第二次了

    见这么个面试题:

    def demo():
        for i in range(4):
            yield i
    
    g=demo()
    
    g1=(i for i in g)
    g2=(i for i in g1)
    
    print(list(g1))
    print(list(g2))

    11.6.2 yield from(两个例子结果相同,比较一下两者区别)

    def func():
        yield from 'AB'
        yield from [1, 2, 3]
    
    g = func()
    print(list(g))
    
    ②
    def func():
        for i in 'AB':
            yield i
        for i in [1, 2, 3]:
            yield i
    
    g = func()
    print(list(g))
  • 相关阅读:
    mysql 主从配置 读写分离
    interface接口
    http结构
    call_user_func函数
    pcntl_fork 进程
    数据库事务
    php 之 ob缓冲
    shell脚本
    php 守护进程
    ssdb zset
  • 原文地址:https://www.cnblogs.com/LearningOnline/p/8463925.html
Copyright © 2020-2023  润新知