迭代器和生成器(难点)

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))
原文地址:https://www.cnblogs.com/LearningOnline/p/8463925.html