python开发基础----迭代器和生成器

什么是迭代器协议

  迭代器协议是指对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么引起一个stopIteration异常,以终止迭代(只能往后不能往前退)

  可迭代对象:实现了迭代器协议的对象(如何实现,对象内部定义了一个__iter__()方法),可迭代对象就是迭代器

  协议是一种约定,可迭代对象实现了迭代器协议,python内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象

 

  

python中强大的for循环机制

  for循环本质:循环所有对象,全都是使用迭代器协议

  注意:字符串、列表、元组、字典、集合、文件对象这些都不是可迭代对象,只不过在for循环时,调用了他们内部的__iter__方法,把他们变成了可迭代对象

  for循环就是基于迭代器协议提供了一个统一的可以遍历所有对象的方法,即在遍历之前,先调用对象的__iter__方法将其转换为一个迭代器,然后使用迭代器协议去实现循环访问,内置函数next()实质上就是在调用对象的__next__方法

s = 'hello' #字符串s是内有next方法的,所以不能说是迭代器对象
s_iter = s.__iter__() # 这是把字符串 s 转换为可迭代对象
#遵循迭代器协议访问方式 print(s_iter.__next__()) print(s_iter.__next__()) print(s_iter.__next__()) print(s_iter.__next__()) print(s_iter.__next__()) #print(s_iter.__next__()) 报StopIteration错误 结果: h e l l o

#使用下标的访问方式
l = [1,2,3]
print(l[0])
print(l[1])
print(l[2])
#print(l[3]) 超出边界报错:IndexError

#使用for循环方式,for本质就是遵循迭代器协议的访问方式,先调用对象的
l = l.__iter__()方法,然后依次执行l.__next__(),知道for循环捕捉到StopIteration终止循环
l = [1,2,3]
for i in l: #实际上执行了2步,1、 l = l.__iter__() 2 l.__next__()
print(i)

while模拟for循环处理
l = [1,2,3,4]
diedai_l = l.__iter__()
while True:
try:
print(diedai_l.__next__())
except StopIteration:
print("迭代完毕!")
break

 


1、迭代器

迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件

特点:

  1. 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
  2. 不能随机访问集合中的某个值 ,只能从头到尾依次访问
  3. 访问到一半时不能往回退
  4. 便于循环比较大的数据集合,节省内存
>>> a = iter([1,2,3,4,5])
>>> a
<list_iterator object at 0x101402630>
>>> a.__next__()
1
>>> a.__next__()
2
>>> a.__next__()
3
>>> a.__next__()
4
>>> a.__next__()
5
>>> a.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

2、生成器

  什么是生成器?

  可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他的数据类型需要调用自己内置的__iter__方法),所以生成器就是可迭代对象

  

  生成器分类及在python中的表现形式(Python有两种不同的方式提供生成器)

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

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

  

  生成器的优点:

  使用生成器对延迟操作提供了支持,所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。这也是生成器的主要好处

  

  总结:

  1、可迭代对象

  2、实现延迟计算,省内存

  3、生成器本质和其他的数据类型一样,都是实现了迭代器协议,只不过生成器附加了一个延迟计算省内存的好处,其余的可迭代对象没有这点好处

 

  三元表达式  

name = 'alex'
name = 'li'
res = 'sb' if name == 'alex' else 'li'   #三元表达式,如果name== 'alex' 就返回'sb'否则返回'li'
print(res)

l = ['鸡蛋%' %x for x in range(10) if x > 5 ] #三种颜色分别代表三元表达式的三元,也就是三次操作,如果再有操作会报错,如下
l = ['鸡蛋%' %x  for x in range(10) if x > 5 else i ] #报错,这有4次操作,超出三元范围

['鸡蛋%' %x  for x in range(10) if x > 5 ] #列表解析器
('鸡蛋%' %x  for x in range(10) if x > 5 ) #生成器

  

age_list = ["鸡蛋%s" % x for x in range(10)]
laomuji = ("鸡蛋%s" %x for x in range(10))
print(laomuji)
print(next(laomuji)) #next本质就是调用__next__
print(laomuji.__next__())
print(next(laomuji))
老母鸡下蛋

 

 

  总结:

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

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

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

  使用生成器

  sum(x ** 2 for x in range(4))

  而不用多此一举的先构造一个列表

  sum([x ** 2 for x in range(4)])

 

一个函数调用时返回一个迭代器,那这个函数就叫做生成器(generator);如果函数中包含yield语法,那这个函数就会变成生成器;

def func():
    yield 1
    yield 2
    yield 3
    yield 4

上述代码中:func是函数称为生成器,当执行此函数func()时会得到一个迭代器。

>>> temp = func()
>>> temp.__next__()
1
>>> temp.__next__()
2
>>> temp.__next__()
3
>>> temp.__next__()
4
>>> temp.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

3、实例

a、利用生成器自定义range

def nrange(num):
    temp = -1
    while True:
        temp = temp + 1
        if temp >= num:
            return
        else:
            yield temp

 

生成器总结

  语法上和函数类似:生成器函数和常规函数几乎是一样的。他们都是使用def语句进行定义,差别在于,生成器使用yield语句返回一个值,而常规函数使用return语句返回一个值

  自动实现迭代器协议:对于生成器,python会自动实现迭代器协议,以便应用到迭代背景中(如for循环、sum函数)。由于生成器自动实现了迭代器协议,所以,我们可以调用它的next方法,并且在没有值可以返回时,生成器自动产生StopIteration异常

  状态挂起:生成器使用yield语句返回一个值。yield语句挂起该生成器函数的状态,保留足够的信息,以使之后从它离开的地方继续执行

 

优点一:生成器的好处是延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据处理,将会非常有用

优点二:生成器还能有效提高代码可读性

def test():
    for i  in range(4):
        yield i

t = test() #注意这里得到的是生成器的内存地址,没有具体的值

t1 = (x for x in t) #这里得到的只是一个生成器的内存地址
t2 = (x for x in t1)

print(list(t1)) #这里因为迭代器的特性,只能往后不能往前,在t1遍历完之后,下面输出t2的时候就为空值了
print(list(t2))

 

 

posted @ 2019-09-04 14:43  Mr-谢  阅读(103)  评论(0)    收藏  举报