Python学习日记(十) 生成器和迭代器

使用dir()我们可以知道这个数据类型的内置函数有什么方法:

print(dir(int))  
print(dir(bool))
print(dir([]))
print(dir({}))
print(dir(set))

 

1.迭代器

iterable:可迭代的

迭代就是将数据能够一个一个按顺序取出来

s = 'abc'
print('__iter__' in dir(s))      #True
li = [1,2]
print('__iter__' in dir(li))     #True
b = False
print('__iter__' in dir(b))      #False
i = 123
print('__iter__' in dir(i))      #False
dic = {}
print('__iter__' in dir(dic))    #True
set1 = set()
print('__iter__' in dir(set1))   #True

上面数据类型返回为真说明它是可以迭代的,反之是不可迭代的

可迭代协议:

就是内部要有一个__iter__()来满足要求

当一个具有可迭代的数据执行__iter__()它将返回一个迭代器的内存地址

print('abc'.__iter__()) #

这里的iterator的意思是迭代器

迭代器协议:

现在有一个列表我们来看看它本身和在执行了__iter__()之后的方法有什么不同:

li = [1,2,3,'a']
print(dir(li))  #['__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']
print(dir(li.__iter__()))   #['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']
'''求两者的差集'''
print(set(dir(li.__iter__())) - set(dir(li)))   #{'__length_hint__', '__setstate__', '__next__'}
__length_hint__()的作用是求元素的长度
__setstate__()的作用是指定索引值从哪里开始迭代
__next__()的作用可以让值一个一个的取出

在之前用到的for循环我们就是用__next__()这种方法进行取值,现在我们可以模拟for来写一个函数:
li = [1,2,3,'a']
def getItem(li):
    iterator = li.__iter__()
    while True:
         print(iterator.__next__())
getItem(li)
# 1
# 2
# 3
# a
#StopIteration 如果找不到元素就会报错

如何处理掉这个异常?

li = [1,2,3,'a']
def getItem(li):
    iterator = li.__iter__()
    while True:
        try:
            print(iterator.__next__())
        except StopIteration:
            break
getItem(li)
# 1
# 2
# 3
# a

迭代器遵循迭代器协议:必须要有__iter__()和__next__()

迭代器的好处:

  a.从容器类型中一个一个的取值,会把所有的值都取到

  b.节省内存的空间

     迭代器并不会在内存中占用一大块内存,而是随着循环每次生成一个,每一次使用__next__()来给值

range():

print(range(10000000))  #range(0, 10000000)

实际上range()在调用的时候并没有真正生成这么多的值,如果真的生成的话那么内存可能会溢出

print('__iter__' in dir(range(10)))     #True   
print('__next__' in dir(range(10)))     #False
from collections import Iterable
from collections import Iterator
print(isinstance(range(10),Iterable))    #True  是一个可迭代对象
print(isinstance(range(10),Iterator))    #False 在执行后得到的结果并不是一个迭代器

迭代器总结:

1.可以被for循环的都是可迭代的

2.可迭代的内部都有__iter__()方法

3.只要是迭代器一定可以迭代

4.可迭代的变量.__iter__()方法可以得到一个迭代器

5.迭代器中的__next__()方法可以一个一个的获取值

6.for循环实际上就是在使用迭代器

 

2.生成器

生成器函数

本质上就是我们自己写的函数,只要含有yield关键字的函数就是生成器函数,yield不能和return共用且需要写在函数内部

def generator():        #生成器函数
    print('a')
    yield 5
ret = generator()       #ret是一个生成器
print(ret)              #

生成器函数每次执行的时候会得到一个生成器作为返回值

如果要返回函数值:

def generator():        #生成器函数
    print('a')
    yield 5
    print('b')
    yield 4
g = generator()        #g是一个生成器
print(g)               #
ret = g.__next__()
print(ret)              #a
                        #5
print('---------')
ret = g.__next__()
print(ret)              #b
                        #4

执行顺序:

Python学习日记(十) 生成器和迭代器_第1张图片

使用for循环遍历生成器:

def generator():        #生成器函数
    print('a')
    yield 5
    print('b')
    yield 4
    print('c')
    yield 6
g = generator()       #g是一个生成器
for i in g:
    print(i)    
# a
# 5
# b
# 4
# c
# 6

send():

send()和__next__()都有获取下一个值的功能,但send()在获取下一个值的时候可以给上一个yield的位置传递一个数据,让用户进行接收

send()在使用时需注意在第一次使用生成器时需用__next__()获取下一个值并且最后一个yield不能接受外部的值

def generator():         #生成器函数
    print('a')           #1
    content = yield 1    #2
    print(content,'= 1') #3
    print('b')           #4
    content2 = yield 2   #5
    print(content2,'= 2')#6
    yield 3              #7

g = generator()          #生成器g
ret = g.__next__()       #执行1和2的右边语句
print(ret)               #返回yield的值1
ret = g.send('a')        #传递给yield一个值'a'让content接收,然后执行3,4语句
print(ret)               #返回yield的值2
ret = g.send('b')        #传递给yield一个值'b'让content2接收,然后执行6语句 yield再返回一个值3
print(ret)
# a
# 1
# a = 1
# b
# 2
# b = 2
# 3

移动平均值例子:

def average():          #定义一个平均值的生成器函数
    sum = 0
    count = 0
    avg = 0
    while True:
        num = yield avg
        sum += num
        count += 1
        avg = sum/count

avg_g = average()       #取得生成器
avg_g.__next__()
avg1 = avg_g.send(int(input()))     #10
avg1 = avg_g.send(int(input()))     #20
print(avg1)                         #15

预激装饰器的生成器

 

def wrapper(func):
    def inner(*args,**kwargs):
        g = func(*args,**kwargs)
        g.__next__()
        return g
    return inner
@wrapper                #wrapper = wrapper(func)
def avgerage():
    sum = 0
    count = 0
    avg = 0
    while True:
        num = yield avg
        sum += num
        count += 1
        avg = sum/count
avg_g = avgerage()
print(avg_g.send(10))
print(avg_g.send(20))
print(avg_g.send(30))

 

yiled form

如何将字符串一个一个的去返回?

一般方法:

def generator():
    a = 'abcde'
    b = 'efdgh'
    for i in a:
        yield i
    for i in b:
        yield i
g = generator()
for i in g:
    print(i)

用yiled form:

def generator():
    a = 'abcde'
    b = 'efdgh'
    yield from a
    yield from b
g = generator()
for i in g:
    print(i)
# a
# b
# c
# d
# e
# e
# f
# d
# g
# h

监听文件例子:

def tail(filename):
    f = open(filename,encoding='utf-8')
    while True:
        line = f.readline()
        if line.strip():
             yield line.strip()
g = tail('tail')
for i in g:
    if 'python' in i:
        print('******',i)   
# ****** python
# ****** asd python

 

3.推导式

优点:基本不消耗内存

语法:以列表推导式为例

   (1)遍历元素进行挨个处理:[每一个元素或者是和元素相关的操作 for 元素 in 可迭代数据类型]  

   (2)筛选功能:[满足条件的元素的相关操作 for 元素 in 可迭代数据类型 if 元素相关的条件]

1.列表推导式

li = [i*i for i in range(5)]
print(li)   #[0, 1, 4, 9, 16]

 

找到50以内能被5整除的数:

li = [i for i in range(50) if i % 5 == 0]
print(li)   #[0, 5, 10, 15, 20, 25, 30, 35, 40, 45]

找到两个列表中都含有一个'x'的元素

names = [['xasd','12sa','xa'],['564','566','x233']]
li = [name for lst in names for name in lst if name.count('x') == 1]
print(li)   #['xasd', 'xa', 'x233']

2.生成器推导式

g = (i*2 for i in [1,5,6,7])
print(g)    # at 0x006E4F00>
for i in g:
    print(i)
# 2
# 10
# 12
# 14

3.字典推导式

将字典的键值互换

dict = {'key1' : 'value1','key2' : 'value2','key3' : 'value3'}
print(dict)         #{'key3': 'value3', 'key2': 'value2', 'key1': 'value1'}
new_dict = {dict[i] : i for i in dict}
print(new_dict)     #{'value2': 'key2', 'value1': 'key1', 'value3': 'key3'} 

合并大小写对应的value值,再将键都统一成小写

dict = {'a' : 1,'b' : 2,'A' : 3,'c' : 4}
new_dict = {k.lower() : dict.get(k.lower(),0) + dict.get(k.upper(),0) for k in dict}    #如果没有找到这个值就返回0
print(new_dict) #{'c': 4, 'b': 2, 'a': 4}

4.集合推导式

具有去重复的功能

set = {x**2 for x in range(-5,6)}
print(set)  #{0, 1, 4, 9, 16, 25}

 

4.相关面试题

例一:

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

g = generator()
g1 = (i for i in g)
g2 = (i for i in g1)
print(list(g1))         #[0, 1, 2, 3]       
print(list(g2))         #[]       因为g2是往g1去拿值,但是g1已经从g用for循环遍历完了值,所以g2是一个空的列表

例二:

def add(n,i):
    return n+i
def test():
    for i in range(4):  #0 1 2 3
        yield i
g = test()
for n in [1,10]:
    g = (add(n,i) for i in g)
'''
n = 1
g = (add(n,i) for i in g)   这里的代码因为跑过了 就不执行
n = 10
g = (add(n,i) for i in (add(n,i) for i in test()))
所以
g = (add(10,i) for i in (add(10,i) for i in [0,1,2,3]))
g = (add(10,i) for i in (10,11,12,13)
g = (20,21,22,23)
'''
print(list(g))           #[20, 21, 22, 23]     当没有调用的时候上面的代码都不会执行

转载于:https://www.cnblogs.com/Fantac/p/11343409.html

你可能感兴趣的:(Python学习日记(十) 生成器和迭代器)