迭代器和生成器

迭代器(iterator) 

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

迭代的概念

#迭代器即迭代的工具,那什么是迭代呢?
#迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值
while True: #只是单纯地重复,因而不是迭代
    print('===>') 
    
L=[1,2,3]
count=0
while count < len(L): #迭代
    print(l[count])
    count+=1

#dir() ----(展示所拥有的所有方法)
print
(dir([1,2])) print(dir((2,3))) print(dir({'a':1,'b':2})) print(dir({1,2,3,4})) ##以下所展示的有双下方法:如'__add__', '__class__'它们是Python解释器的内部方法,也称为双下方法。是由C语言写的,可能不止一种方式调用它。
# ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] # ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index'] # ['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values'] # ['__and__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__iand__', '__init__', '__init_subclass__', '__ior__', '__isub__', '__iter__', '__ixor__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update']

##双下方法:

print([1].__add__([2]))
print([1]+[2])
###
[1, 2]
[1, 2]

 由上可以看出,可以被for循环的都是可迭代的,要想可迭代,内部必须要有一个__iter__方法。

首先,判断 int 类型有没有__iter__方法:

print("__iter__" in dir(int))
# False

##执行以下__iter__方法:

print([1,2].__iter__())

# <list_iterator object at 0x000001A68B0210B8> #生产一个迭代器

得到了一个list_iterator,此时我们又得到了一个新的名词——iterator。(迭代器)

同样的,可以对字典、集合、元祖进行__iter__操作

print((1,2,3).__iter__())
print({'a':1,'b':2}.__iter__())
print({1,2,3}.__iter__())

# <tuple_iterator object at 0x0000021688F61080>
# <dict_keyiterator object at 0x0000021688E8A4A8>
# <set_iterator object at 0x0000021688F60630>

同样的得到了tuple_iterator、dict_keyiterator、set_iterator,这就说明了它们都是可迭代的。

可以通过集合求差集来看迭代器与可迭代对象质检的差别。

print(set(dir([1,2].__iter__()))-set(dir([1,2])))

# {'__next__', '__length_hint__', '__setstate__'}


迭代器:必须拥有__iter__方法和__next__方法

print("__iter__" in dir([].__iter__()))
print("__next__" in dir([].__iter__()))

###
True
True
############
from
collections import Iterable from collections import Iterator print(isinstance([],Iterator)) print(isinstance([],Iterable )) ############ False True 列表是可迭代的,但不是一个迭代器

#####或者创造一个数据类型:

from collections import Iterable
from collections import Iterator
class A:
    def __iter__(self):pass
    def __next__(self):pass
a = A()
print(isinstance(a,Iterator))
print(isinstance(a,Iterable ))

###
True
True
再次说明,
迭代器:必须拥有__iter__方法和__next__方法
PS:可迭代的.__iter__()方法就可以得到一个迭代器
  迭代器中的__next__()方法可以一个一个获取值

######################################补充说明###############################################

#1、为何要有迭代器?
对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器

#2、什么是可迭代对象?
可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下
'hello'.__iter__
(1,2,3).__iter__
[1,2,3].__iter__
{'a':1}.__iter__
{'a','b'}.__iter__
open('a.txt').__iter__

#3、什么是迭代器对象?
可迭代对象执行obj.__iter__()得到的结果就是迭代器对象
而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象

文件类型是迭代器对象
open('a.txt').__iter__()
open('a.txt').__next__()


#4、注意:
迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象

迭代器的作用:

dic={'a':1,'b':2,'c':3}
iter_dic=dic.__iter__() #得到迭代器对象,迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身
iter_dic.__iter__() is iter_dic #True

print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
# print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志

#有了迭代器,我们就可以不依赖索引迭代取值了
iter_dic=dic.__iter__()
while 1:
    try:
        k=next(iter_dic)
        print(dic[k])
    except StopIteration:
        break
        
#这么写太丑陋了,需要我们自己捕捉异常,控制next,python这么牛逼,能不能帮我解决呢?能,请看for循环

for循环:(for循环其实就是在使用迭代器)

#基于for循环,我们可以完全不再依赖索引去取值了
dic={'a':1,'b':2,'c':3}
for k in dic:
    print(dic[k])

#for循环的工作原理
#1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic
#2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码
#3: 重复过程2,直到捕捉到异常StopIteration,结束循环

 迭代器的优缺点:

#优点:
  - 提供一种统一的、不依赖于索引的迭代方式 (方便使用,从容器数据类型中一个一个取值,并把所有的值都取到- 惰性计算,节省内存(迭代器并不会在内存中占用一大块内存来保存数据,而是随着循环,每次只生成一个数据)
#缺点:
  - 无法获取长度(只有在next完毕才知道到底有几个值)
  - 一次性的,只能往后走,不能往前退

 #只有可迭代对象的时候才能用for循环

#当遇到一个变量,不确定是否能用for循环是,先判断它是否可迭代

#############################################################################################

生成器

什么是生成器(生成器的本质是迭代器)


#只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码
#含有yield关键字的函数就是 生成器函数
#调用生成器函数的时候,函数不执行,而是返回一个生成器
#每次调研阶段next方法,就会取到一个值得
#直达取到最后一个,再采用next方法就会报错

def
generator():
print('====>first')
    yield 1
    print('====>second')
    yield 2
    print('====>third')
    yield 3
    print('====>end')

g = generator()
print(g) #<generator object generator at 0x00000299A9D16AF0>
print(g.__next__())
print(g.__next__())
print(g.__next__())
################
或者:
for i in g:
  print(i)
##### <generator object generator at 0x00000299A9D16AF0> ====>first 1 ====>second 2 ====>third 3

 ##wahaha:

# 哇哈哈%i
def wahaha():
    for i in range(100):
        yield "哇哈哈%d"%i

g = wahaha()
count = 0
for i in g:
    count += 1
    print(i)
    if count > 10:
        break
print("****",g.__next__()) #生成器记录当前的位置以及下一步要走的位置
###
哇哈哈0
哇哈哈1
哇哈哈2
哇哈哈3
哇哈哈4
哇哈哈5
哇哈哈6
哇哈哈7
哇哈哈8
哇哈哈9
哇哈哈10
**** 哇哈哈11
#监听过滤文件,文件有“Python”就打印
def tail(filename):
    f = open(filename,encoding="utf-8")
    while 1:
        line = f.readline()
        if line.strip():
            yield line.strip()

g = tail("file")
for i in g:
    if "python" in i:
        print("$$$$$",i)

 生成器表现形式:

1.生成器函数

#含有yield关键字的函数就是生成器函数

#特点:调用函数之后,函数不执行,而是返回一个生成器;每次调用next方法的时候会取到一个值;直到取到最后一个,再执行next会报错

2.生成器表达式

#从生成器中取值的方法:next;for;数据类型的强制转换,占内存。

生成器进阶:

send获取下一个值的效果和next基本一样只是在获取下一个值的时候,给上一个yield的位置传递一个数据

使用send的注意事项

1)、send作用范围和next一样

2)、第一次使用生成器的时候,是用next方法获取下一个值

3)、最后一个yield不能接受外部的值

def generator():
    print(123)
    content=yield 1
    print("========",content)
    arg = yield 2
    ''''''
    yield 

g = generator()
ret = g.__next__()
print("$$$$$$$$",ret)
ret = g.send("Hello!") # send 的效果和next一样
print("########",ret)
###
123
$$$$$$$$ 1
======== Hello!
######## 2

##用生成器,获取移动平均值#移动平均值

def average():
    sum = 0
    count = 0
    avg = 0
    while 1:
        num = yield avg
        sum += num
        count += 1
        avg = sum/count
avg_g = average()
avg_g.__next__()
print(avg_g.send(10))
print(avg_g.send(20)
print(avg_g.send(30))
### 
10.0
15.0
20.0
#带装饰器的移动平均值;#在调用被装饰生成器函数的时候首先用next激活生成器
#预激生成器: def init(func): def inner(*args,**kwargs): g = func(*args,**kwargs) next(g) #g.__next__() return g return inner @init def average(): sum = 0 count = 0 avg = 0 while 1: num = yield avg sum += num count += 1 avg = sum/count avg_g = average() print(avg_g.send(10)) print(avg_g.send(20)) print(avg_g.send(30))
### 
10.0
15.0
20.0
#在调用被装饰生成器函数的时候首先用next激活生成器

 ###yield from

##
def gen1():
for c in 'AB':
yield c
for i in range(3):
yield i
print(list(gen1())) #数据类型的强制转换
#['A', 'B', 0, 1, 2]
##
def gen2():
yield from 'AB'
yield from range(3)

print(list(gen2()))
##

['A', 'B', 0, 1, 2]


#
python3的新功能(yield from) def generator(): a = "AB" b = "22" yield from a yield from b g = generator() for i in g: print(i) ## A B 2 2

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

#列表推导式
egg_list = ["egg %d" %i for i in range(10)]
print(egg_list)

#或者用for循环
egg_list = []
for i in range(10):
    egg_list.append("egg %d" %i)
print(egg_list)

print([i*i for i in range(10)])
###
['egg 0', 'egg 1', 'egg 2', 'egg 3', 'egg 4', 'egg 5', 'egg 6', 'egg 7', 'egg 8', 'egg 9']
['egg 0', 'egg 1', 'egg 2', 'egg 3', 'egg 4', 'egg 5', 'egg 6', 'egg 7', 'egg 8', 'egg 9']
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

##生成器表达式

#生成器表达式
g = (i for i in range(10))
print(g) #返回一个生成器,几乎不占用内存
for i in g:
    print(i)
##
<generator object <genexpr> at 0x00000262E6CE6308>
0
1
2
3
4
5
6
7
8
9

 ###各种推导式:

[满足条件的元素  for  元素  in  可迭代的数据类型  if  元素满足的相关条件 ] #筛选功能

例一:30以内所有能被3整除的数

multiples = [i for i in range(30) if i % 3 is 0]
print(multiples)
# Output: [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]

例二:30以内所有能被3整除的数的平方

def squared(x):
    return x*x
multiples = [squared(i) for i in range(30) if i % 3 is 0]
print(multiples)

或者:
lst = [i*i for i in range(30) if i % 3 ==0]
print(lst)
##
[0, 9, 36, 81, 144, 225, 324, 441, 576, 729]

例三:找到嵌套列表中名字含有两个‘e’的所有名字

names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
         ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]

print([name for lst in names for name in lst if name.count('e') >= 2])  # 注意遍历顺序,这是实现的关键
 
#['Jefferson', 'Wesley', 'Steven', 'Jennifer']

原文地址:https://www.cnblogs.com/shaopan/p/10015997.html