python的迭代器和生成器

本文深入探讨了Python中的迭代器和生成器,包括推导式(列表、字典、集合)、可迭代对象的概念和确认方法、迭代器的工作原理及其优势。重点介绍了生成器的使用,如生成器表达式、yield关键字、send数据和yieldfrom,展示了如何通过生成器优化内存效率。此外,还提供了多个实例和练习,帮助理解它们在处理大规模数据时的优势。
摘要由CSDN通过智能技术生成

python的迭代器和生成器

推导式

推导式comprehensions(又称解析式),是Python的一种独有特性。推导式是可以从一 个数据序列构建另一个新的数据序列的结构体。 共有三种推导,在Python2和3中都有支 持:

 列表(list)推导式

 字典(dict)推导式

 集合(set)推导式

 列表(list)推导式

用[]生成list 基本格式:

variable = [out_exp_res for out_exp in input_list if out_exp == 2]

  • out_exp_res: 列表生成元素表达式,可以是有返回值的函数。
  • for out_exp in input_list: 迭代input_list将out_exp传入out_exp_res表达式中。
  • if out_exp == 2: 根据条件过滤哪些
[i*i for i in xx if i%3]
意义:
 for i in xx:
 	if i%3:
 		i*i
result=[i*i for i in range(20) if i%3]

tmp=[]
for i in range(20):
    if i%3:
        tmp.append(i*i)
print(result)
print(tmp)
输出
[1, 4, 16, 25, 49, 64, 100, 121, 169, 196, 256, 289, 361]
[1, 4, 16, 25, 49, 64, 100, 121, 169, 196, 256, 289, 361]
#使用列表推导式输出200以内,开平方是整数的数
import math
result=[math.sqrt(i) for i in range(200) if (math.sqrt(i)%1) == 0]
print(result)
#读列表中的数取两位小数
lis=[2.45345,4.3455325,82.234324,9.841234]
result2=["%.2f"%j for j in lis ]
result3=[float(int(j*100)+1)/100 if (int(j*1000)%10)>=5 else  float(int(j*100))/100 for j in lis ]
print(result2)
print(result3)

 三元运算与列表推导

print([i//2 if i%2==0 else i for i in range(100) if i % 3 == 0 ])

推导式嵌套

• 找到嵌套列表中名字含有2个’e’的所有名字

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

names = [['Tom' , 'Billy' , 'Jefferson' , 'Andrew' , 'Wesley' , 'Steven' , 'Joe'], ['Alice' , 'Jill' , 'Ana' , 'Wendy' , 'Jennifer' , 'Sherry' , 'Eva' , 'Elven']]
for i in names:
    for j in i:
        if 'e'in j.lower():
            num=0
            for k in j:
                if k=='e' or k=='E':
                    num+=1
                if num>=2:
                    print(j)
                    break

        # if j.lower().count('e')>=2:
        #     print(j)
列表推导式:
print([name for lst in names for name in lst if name.lower().count('e')>=2])

 字典(dict)推导式

字典推导和列表推导的使用方法是类似的,只不中括号该改成大括号。

基本格式:variable = {out_key:out_value for out_key,out_value in input_list if out_exp == 2}

  • out_key: 返回字典结果的key
  • out_value: 返回字典结果的value
  • for out_key,out_value in input_list:迭代input_list将out_exp传入out_exp_res表达式中。
  • if out_exp == 2:根据条件过滤哪些值可以
print({x:y for x,y in [('a',1),('b',2)]})
输出:
{'a': 1, 'b': 2}
  • 统计字符串中每一个字符出现的次数

    str1="jalkefjf327akjfioahioeru"
    print({x:str1.count(x) for x in set(str1)})
    for i in str1
    	tmp[i]=tmp.get(i,0)+1
    

集合(set)推导式

它们跟列表推导式也是类似的。 唯一的区别在于它使用大括号{}。

  • 集合推导式,自带去重功能
str1="afjdsjafjkl"
print(set(str1))
print({i for i in str1})
输出:
{'a', 'l', 'd', 'f', 's', 'j', 'k'}
{'a', 'l', 'd', 'f', 's', 'j', 'k'}

练习:

过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母

• q1 = [‘a’ , ‘ab’ , ‘abc’ , ‘abcd’ , ‘abcde’]

print([x.upper() for x in q1 if len(x)>=3])

 求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元组列表

• 期望结果:[(0,1),(0,3),(0,5), (2,1), (2,3)…]

print([(x,y) for x in range(6) for y in range(6) if x%2==0and y%2!=0])

 快速更换key和value # 期望结果:{10:‘a’ , 34:‘b’}

• q3 = {‘a’: 10, ‘b’: 34}

q3 = {'a': 10, 'b': 34}
print({q3[y]:y for y in q3 })
print({y:x for x,y in q3.items() })

 合并大小写对应的value值,将k统一成小写 #期望 结果:{‘a’:5, ‘b’:9, ‘c’:3}

• q4 = {‘B’:3, ‘a’:1, ‘b’:6, ‘c’:3, ‘A’:4}

q4 = {'B': 3, 'a': 1, 'b': 6, 'c': 3, 'A': 4}
print({x.lower():q4.get(x.lower(),0)+q4.get(x.upper(),0) for x in q4})

python的可迭代对象

for 后面接可迭代对象

• 之前讲过的for可用的有哪些?

• list, set, str, dict, tuple

• list(可迭代对象)

• sorted(可迭代对象

容器(container)

容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in, not in关键字判断元素是否包含在容器中。

通常这类数据结构把所有的元素存储在内存中(也有一些特例,并不是所有的元素都放在内存,比如 迭代器和生成器对象)

 在Python中,常见的容器对象有

• str,list, deque, tuple , …

• dict, defaultdict, OrderedDict, Counter, ….

• set, frozensets, …

可迭代对象

凡是可以返回一个迭代器的对象都可称之为可迭代对象。

如何确认一个对象是不是可迭代对象

  • 可迭代对象实现了__iter__方法,该方法返回一个迭代器对象

  • 使用Iterable判断

  • img

class A:
    def __iter__(self):
        return None
a1=A()
from collections import Iterable
print(isinstance('abc',Iterable))
print(isinstance(a1,Iterable))

可迭代对象有哪些

  • 容器都是可迭代对象
  • 此外还有更多的对象同样也是可迭代对象,比如处于打开状态的files,sockets,range等等。
  • 凡是可以返回一个迭代器(__iter__)的对象都可称之为可迭代对象

Python迭代器(iterator)

 什么迭代器

这里x是一个可迭代对象,可迭代对象和容 器一样是一种通俗的叫法,并不是指某种具 体的数据类型

• list是可迭代对象

• dict是可迭代对象

• set也是可迭代对象。

• y和z是两个独立的迭代器, 迭代器有一种具 体的迭代器类型,比如list_iterator, set_iterator。

  • img

迭代器是有状态的,可以被next()调用,函数调用并不断返回下一 个值的对象称为迭代器(Iterator)。

迭代器一定是一个可迭代对象

任何实现了__iter__()__next__()都是迭代器

__iter__():返回迭代器自身

__next__():返回容器中的下一个值

如果容器中没有更多元素了,则抛出StopIteration异常

• (for循环取数据遇到StopIteration时,会退出循环)

• 所有的Iterable均可以通过内置函数iter()来转变为迭代器

#for循环---先调用对象的__iter__方法得到一个迭代器
#在调用__next__方法,不断返回下一个值
#容器中没有更多的元素了,则抛出StopIteration
for i in 'abc':
    print(i)
列表是一个可迭代对象不是一个迭代器
print(dir([1,2,3]))
print(hasattr([1,2,3],"__next__"))---False

lst=[1,2,3]
lst_it=lst.__iter__()
lst_it2=iter(lst)
print(hasattr(lst_it,"__next__"),hasattr(lst_it,"__iter__"))
print(lst_it.__next__())
print(next(lst_it))
print(next(lst_it))
print(next(lst_it))
输出:
True True
1
2
3
Traceback (most recent call last):
  File "D:\运维开发\python\python项目\2022-7-8迭代式和生成器\2.可迭代对象.py", line 28, in <module>
    print(next(lst_it))
StopIteration

判断迭代器

• 看是否实现了__iter____next__方法

• Iterator判断

  • img

迭代器有什么好处

迭代器就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休 眠状态等待下一次调用

 可迭代对象与迭代器的转换

#懒加载---》惰性求值 用的时候才生成(迭代器-节省空间的方案)
#oom out of memory

• 列表对象转迭代器

  • img
#编写迭代器实现range
class MyRange():
    k=-1
    def __init__(self,num):
        self.num=num
    def __iter__(self):
        return self
    def __next__(self):
        if self.k < self.num:
            self.k+=1
            return self.k
        else:
            raise StopIteration


a1=MyRange(5)
print(a1.__next__())
print(next(a1))
print(next(a1))
print(next(a1))
print(next(a1))
print(next(a1))
print(next(a1))


#编写迭代器实现斐波那契数列
class fbnqsl():
    i=j=1
    def __init__(self,num):
        self.num=num
    def __iter__(self):
        return self
    def __next__(self):
        self.k=0
        if self.k < self.num:
            self.k=self.i+self.j
            self.i=self.j
            self.j=self.k
            return self.k
        else:
            raise StopIteration
lst=fbnqsl(20)
print(lst.__next__())
print(lst.__next__())

• 迭代器转列表对象

  • img

 生成无限序列

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W8AgSMSN-1659701521260)(https://api2.mubu.com/v3/document_image/34a3e170-d324-4eb7-9f68-e51fd8d3fe47-17377742.jpg)]

  • img

  • img

Python生成器(genatator)

什么是生成器-generator

生成器算得上是Python语言中最吸引人的特性之一,生成器

其实是一种特殊的迭代器, 不过这种迭代器更加优雅

它不需要手动编写__iter__()__next__()方法,只需要一个 yiled关键字。

• 生成器一定是迭代器(反之不成立)

• 因此任何生成器也是以一种懒加载的模式生成值

 生成器表达式

使用()生成generator, 将俩表推导式的[]改成()即可得到生成器。

• 列表推导式与生成器表达式,定义和使用非常类似,但是,在(大量数据处理时)性能

上相差很大

#1.生成器表达式
lst=[4,5,6]
g1=(x*2 for x in lst)
print(next(g1))

yield关键字

包含yield表达式的函数是特殊的函数,叫做生成器函数(generator function),被调用时将返回一个 迭代器(iterator),调用时可以使用next或send(msg)

• 一个生成器中可以有多个yield

• 一旦遇到yield,就会保存当前状态,然后返回yield后面的值

#2.生成器函数
def get_count():
    print(("start yield...."))
    yield 3
    print("second yield..")
    yield 4
    print("end...")
g1=get_count()
print(next(g1))---只运行到第一个yield,下次运行就会运行到下一个yield
print(dir(g1))--验证是否为迭代器
输出:
start yield....
3

  • img

yield关键字的运行规则

  • • 当生成器遇到一个yield时,会暂停运行生成器,返回yield后面的值。

    ​ 当调用next是,遇到yield就暂停运行,并且放回yield后面表达式的值

  • • 当再次调用生成器的时候,会从刚才暂停的地方继续运行,直到下一个yield。

  • • yield关键字:保留中间算法,下次继续执行

  • yield只能写在函数内部

使用生成器实现range
def Myrange(num):
    i=0
    while i<num:
        yield i
        i+=1
for i in Myrange(10):
    print(i)
#使用生成器实现斐波那契数列

生成器 fib() 的执行过程

• 当执行f=fib()返回的是一个生成器对象,此时函数体中的代码并不会执行,而是首先返回一个 iterable 对象!

• 只有显示或隐示地调用next的时候才会真正执行函数里面的代码

• 执行到语句 yield b 时,fab() 函数会返回yield后面(右边)的值,并记住当前执行的状态

• 下次调用next时,程序流会回到 yield b 的下一条语句继续执行

• 看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返 回当前的迭代值。

• 由此可以看出,生成器通过关键字 yield 不断的将迭代器返回到内存进行处理,而不会一次性 的将对象全部放入内存,从而节省内存空间

send数据

除了可以使用 next() 方法来获取下一个生成的值,用户还可以使用 send() 方法将一个 新的或者是被修改的值返回给生成器。除此之外,还可以使用 close() 方法来随时退出 生成器。

send就是改变生成器的值

def counterf():
    count=1
    while 1:
        val = yield count
        print(f"val is {val}")
        if val is not  None:
            print(f"val is {val}")
            count=val
        else:
            count+=1
count=counterf()
print(next(count))
count.send(10)#运行send修改数据,必须要先激活生成器,可以调用next激活
print(count.send(10))#返回一个新的值给yield count
print(next(count))
输出:
1
val is 10
val is 10
val is 10
val is 10
10
val is None
11
#close用法
#手动关闭当前生成器,关闭后不能再继续使用生成值
count.close()
print(next(count))
输出:
Traceback (most recent call last):
  File "D:\运维开发\python\python项目\2022-7-8迭代式和生成器\2.可迭代对象&迭代器.py", line 132, in <module>
    print(next(count))
StopIteration
  • img

yield from

def f1():
    yield range(10)
def f2():
    yield from range(10)#yield from 只能返回可迭代对象
#     类似于for循环
#     for item in range(10):
#         yield item
#     yield from 10#后面的返回值必须是可迭代对象
        
iter1=f1()
iter2=f2()
print(iter1)
print(iter2)
print(next(iter1))#已经返回完了
# print(next(iter1))#所以就无法再取值了
print(next(iter2))
print(next(iter2))
输出:
<generator object f1 at 0x000001CD32F9ED50>
<generator object f2 at 0x000001CD32F9EC00>
range(0, 10)
0
1


def g1(x):
yield range(x)
def g2(x):
yield from range(x)
it1 = g1(5)
it2 = g2(5)
print( [ x for x in it1] )
#out [range(0, 5)]
print( [ x for x in it2] )
#out [0, 1, 2, 3, 4]

使用生成器最好的场景

当你需要以迭代的方式去处理一个巨大的数据集合。比如:一个巨大的文件/一个复杂的数据库查 询等

  • img

如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的 缓冲区来不断读取文件的部分内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松 实现文件读取

#逐行读取文件内容
fpath='D:\运维开发\python\python项目\weblog.txt'
with open(fpath) as f:#默认就为读所以不用写模式
    for i in f:#但这种方式读取到内存中会很慢
        print("*"*20)
        print(i,end="")#因为print会默认加\n换行符
        

生成器的好处

生成器在Python中是一个非常强大的编程结构

• 可以用更少地中间变量写流式代码

• 相比其它容器对象它更能节省内存

• 可以用更少的代码来实现相似的功能

重构你的代码

现在就可以动手重构你的代码了,但凡看到类似:

def something():
	result = []
	for ... in ...:
		result.append(x)
	return result

都可以根据实际需求用生成器函数来替换:

def iter_something():
	for ... in ...:
		yield 

可迭代对象、迭代器、生成器

可迭代对象:实现了__iter__, 返回了一个迭代器对象。

 迭代器:Python中一个实现了_iter_方法和_next_方法的类对象,就是迭代器

 生成器:延迟操作。也就是在需要的时候才产生结果,不是立即产生结果。

• 生成器是只能遍历一次的。

• 生成器是一类特殊的迭代器

三种关系小结

• 对象是可迭代对象, 但却不一定是迭代器

• 如果对象属于迭代器, 那么这个对象一定是可迭代的

• 迭代器实现了一个__next__()方法,可以通过next函数每次取出迭代器中的每个元素

• 迭代器对象:iter()返回的是迭代器对象自身。 ‘

• 生成器是一种特殊的迭代器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8JJiFcmv-1659701521265)(C:\Users\50783\AppData\Roaming\Typora\typora-user-images\image-20220708190629753.png)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值