迭代器和生成器

迭代器

再说迭代器之前先来了解下for循环。

首先,我们对一个列表进行for循环。

for i in [1,2,3,4]:  
    print(i)

上面这段代码肯定是没有问题的,但是我们换一种情况,来循环一个数字1234试试

复制代码
for i in 1234
    print(i) 

结果:
Traceback (most recent call last):
  File "test.py", line 4, in <module>
    for i in 1234:
TypeError: 'int' object is not iterable
复制代码

结果是报错,TypeError: 'int' object is not iterable”,说int类型不是一个iterable。

那什么是可迭代的呢 ?

首先,之前的例子说明int,是不可迭代的。那么如果“可迭代”,就应该可以被for循环了。

这个我们知道呀,字符串、列表、元组、字典、集合以及range()都可以被for循环,说明他们都是可迭代的。

即如果可以将某个数据集内的数据“一个挨着一个的取出来”,就叫做迭代,那么是如何判断一个数据类型那个是可迭代的,那个又是不可迭代的呢?

这里就需要引用一个函数dir:可以用来查询一个数据类型所有的方法。


print(dir([1,2]))
print(dir((1,2)))
print(dir({1:2}))
print(dir({1,2}))     
结果显示
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__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__', '__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__', '__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__', '__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']


不难发现,它们中都含有__iter__,总结一下我们现在所知道的:可以被for循环的都是可迭代的,要想可迭代,内部必须有一个__iter__方法。

也就得到了一个名词,可迭代协议:主要含有__iter__方法的数据类型,都是可迭代的。

那么__iter__方法可以做什么呢?

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

结果
<list_iterator object at 0x1024784a8>        列表[1,2]通过__iter__()的方法得到了一个迭代器iterator。

那什么是迭代器比列表又多了什么方法呢?

dir([1,2].__iter__())是列表迭代器中实现的所有方法,dir([1,2])是列表中实现的所有方法,都是以列表的形式返回给我们的,为了看的更清楚,我们分别把他们转换成集合,
然后取差集。
'''
#print(dir([1,2].__iter__()))
#print(dir([1,2]))
print(set(dir([1,2].__iter__()))-set(dir([1,2])))

结果:
{'__length_hint__', '__next__', '__setstate__'}      可以看到,相比于列表,迭代器多了三个方法分别是
iter_l = [1,2,3,4,5,6].__iter__()

#获取迭代器中元素的长度 print(iter_l.__length_hint__())
#根据索引值指定从哪里开始迭代 print('*',iter_l.__setstate__(4))
#一个一个的取值
print('**',iter_l.__next__()) print('***',iter_l.__next__())
这其中如果要使for实现遍历循环的话,就必须要用到__next__()方法了

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

迭代器一定可迭代,可迭代的只需通过调用__iter_()方法,就可以得到迭代器。

迭代器的特点:使用很方便/节省内存空间、且一个迭代器所有的数据只能取一次,取完就没有了。

      

# num = [1,3,5,6,7,8]
# a=(filter(lambda x:x%2==0,num))   #生成一个迭代器
# for i in a:
#     print(i)     这里可以得到  6,8
#print(list(a))   #这里等到的是[]    因为迭代器内的值都被上面for循环取完了

 

查看可迭代和迭代器的方法

    from collections import Iterable,Iterator
        s1='123'
        list1=[1,2,3]
        tuple1=(1,2,3)
        dic1={'a':1}
        set1={1,2,3}
        f=open('a.txt')

        #都是可迭代,只要对象有__iter__()方法,都是可迭代的
        s1.__iter__()
        list1.__iter__()
        tuple1.__iter__()
        dic1.__iter__()
        set1.__iter__()
        f.__iter__()
               
        #查看是否可以迭代的
        print(isinstance(s,Iterable))

        #查看是否是迭代器,只有文件是迭代器
        f.__next__()

        print(isinstance(f,Iterator))

    

生成器

 生成器是一个包含yield关键字的函数,当它被调用时,在函数中的代码不会执行,而会返回一个迭代器。每次请求一个值,就会执行生成器中的代码,直到遇到一个yield或者return语句,
yield语句意味着会生成一个值,return语句一位着生成器要停止执行(不在生成任何东西,return语句只有在一个生成器中使用时才能进行无参数调用)换句话说,生成器是由两部分组成:
生成器的函数和生成器的迭代器,生成器的函数使用def定义,包括yield部分,生成器的迭代器是这个函数返回的部分.

生成器的本质就是迭代器

生成器的表现形式 1.生成器函数

         2.生成器表达式

生成器函数:含有yield的函数就是生成器函数

生成器函数的特点:1.调用函数之后,函数不执行,返回一个生成器。

         2.每次调用__next__()函数的时候可以取一行值,遇到yield停止。   #yield每调用一次取一个值,return则是一次性取完值

           3.知道取完最后一个,再执行会报错。

# def generator():
#     for i in range(20):
#         yield "新年好%d"%i
# g=generator()    #调用生成器函数得到一个生成器
# k=g.__next__()   #每一次执行g.__next__就是从生成器中取值,预示着生成器函数中的代码继续执行
# print(k)
# k1=g.__next__()
# print(k1)
# for i in g: #for循环可以一次性都显示出来 # print(i)

从生成器中取值的几个方法
     next
     for
     数据类型的强制转换 : 占用内存

send:

def generator():        #定义生成器
    print(123)
    content = yield 1
    print('=======',content)
    print(456)
    yield2

g = generator()  
g1= generator() #这里g1和g不相关,因为每次函数调用生成器函数,都会生成一个新的生成器 ret = g.__next__() print('***',ret) ret = g.send('hello') #send的效果和next一样 send将‘hello’传递给yield1 即content print('***',ret)
#send 获取下一个值的效果和next基本一致 #只是在获取下一个值的时候,给上一yield的位置传递一个数据 #使用send的注意事项 # 第一次使用生成器的时候 是用next获取下一个值,send # 最后一个yield不能接受外部的值

send相比于next因其传递数据的特点,有了很多用法,例如求平均数。

def averager():
    total = 0
    count = 0
    average = None
    while True:
        num = yield average      
        total += num
        count += 1
        average = total/count


g = averager()
g.__next__()
print(g.send(10))     #发送数据给num
print(g.send(30))
print(g.send(5))

另外在python3以上版本,可以使用yield from:

#python 3
# def generator():
#     a = 'abcde'
#     b = '12345'
#     for i in a:
#         yield i
#     for i in b:
#         yield i
# def generator():
#     a = 'abcde'
#     b = '12345'
#     yield from a        # yield from的作用和for循环情况下调用yield是一样的
#     yield from b      
#
# g = generator()
# for i in g:
#     print(i)


a
b
c
d
e
1
2
3
4
5

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

# #30以内所有能被3整除的数
# ret = [i for i in range(30) if i%3 == 0]  #完整的列表推导式
# print(ret) #
g = (i for i in range(30) if i%3 == 0) #完整的生成器推导式 #这里for之前的i,可以进行相关操作,比我i**2等
# for i in g
#  print(i)

列表推导式得到的是[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]它是将列表一次性以列表的形式表达出来,而生成器表达式的结果则是将其中的元素一个一个的取出来,这样就生成器表达式节省了内存。

其他数据类型的推导式:

字典推导式:

# 例1:将一个字典的key和value对调
# func = {'a': 10, 'b': 34}
# b = {func[k]: k for k in func}     #通过字典的键进行for循环
# print(b)
# {10:'a' , 34:'b'}
# 例二:合并大小写对应的value值,将k统一成小写
# dic = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
# #{'a':10+7,'b':34,'z':3}
# b = {k.lower(): dic.get(k.lower(), 0) + dic.get(k.upper(), 0) for k in dic}
# print(b)

 

集合推导式:自带结果去重功能

# set = {x**2 for x in [1, -1, 2]}
# print(set)
结果是{1,4}

原文地址:https://www.cnblogs.com/liusouthern/p/8178813.html