迭代指的是一个重复过程,并非单纯的重复,可以说是在原来基础上做出的一些改动。迭代器指的就是迭代取值的过程
例如:
count = 1
while count < 5:
count += 1
# 每次都在原来值的基础上进行了+1,这也是一种迭代的过程
找到一种通用迭代取值的方案,请暂时忘记for循环的使用
下列代码可以把传递的值通过索引打印出来,可以对列表、元组、字符串等进行操作
def ergodic(value):
count = 0
while count < len(value):
print(value[count])
count += 1
ergodic("1234")
>>> 1
>2
>3
>4
# 那么问题来了,我们如果想要对字典,集合等类型数据进行操作呢,是否可以通过这种方式?不行的
这也是我们为什么要了解迭代器的原因,了解for循环都知道,传递什么值它都是可以一一拿出来的,因为for底层原理就是迭代器。
内置有__iter__
方法的数据类型都可以称之为可迭代对象
只要我们调用了__iter__
方法,就会将该类型转换成迭代器对象
# 定义方式
res = value.__iter__()
可迭代对象Iterable
,我们熟悉数据类型的都在:
"Hello".__iter__()
[1,2,3].__iter__()
{
"k1":111}.__iter__()
(1,2,3).__iter__()
{
1,2,3}.__iter__()
# 特殊!
f = open('a.txt','w')
# 默认就是迭代器对象,因为文件来自于硬盘,如果一个文件容量特别大甚至超过内存大小,
# 一次性打开会将内存撑满导致严重问题,所以Python给文件对象制作成了迭代器
我们也可以使用iter()
方法,将可迭代对象转换成迭代器对象Iterator
制作迭代器对象
lis = [1,2,3,4]
iter_lis = iter(lis)
print(iter_lis)
>>> <list_iterator object at 0x7fa3864443d0> # 迭代器对象
迭代器取值是通过__next__()
方法或者next()
方法通常使用next()
print(next(iter_lis)) # 第一次取迭代器对象里面的第一个值 1
print(next(iter_lis)) # 2
print(next(iter_lis)) # 3
print(next(iter_lis)) # 4
print(next(iter_lis)) # 迭代器里面的值已经被取完,如果再进行取值会报错如下
>>> StopIteration # 迭代器没有值可取
使用迭代器对象对字典进行取值
dic = {
'name':'tom',"age":18,'gender':'male'}
iter_dic = iter(dic)
print(next(iter_dic))
>>> 'name' # 可以取出字典的key
那么我们可以通过while对字典进行迭代取值
while True:
try:
key = next(iter_dic)
print(key,dic[key])
except StopIteration:
break
# try未报错执行的代码,except 根据报错的信息执行的代码
# 这里如果出现StopIteration的话,就说明迭代对象的值取完了,那么就结束循环
1、内置有__next__方法
2、内置有__iter__方法,问题是,iter方法就是用于将可迭代对象转换成迭代器对象,那么迭代器里面具备这个方法的作用是?
了解一下for循环的原理
1、调用可迭代对象__iter__方法,拿到迭代器对象
2、调用__next__,将返回值赋值给我们定义的变量item
3、重复步骤2,直到抛出异常StopIteration,for会检测异常然后退出循环
不管传递的是否为迭代器对象,for都会调用__iter__方法,如果迭代器没有__iter__方法
那么for循环调用时岂不就报错了?所以迭代器内的__iter__方法是给for循环使用的
优点:
1、提供了一种不依赖于索引取值,通用迭代取值方案
2、惰性计算、每次只取一个值、节省内存(主要针对文本文件、数据库文件等等,如果没有迭代器那么文件的内容就需要一次性全部读入内存)
缺点:
1、取值不便利,
2、无法预测值的长度(在可迭代对象时期可以通过len查看)
3、一次性取值,每次都只能取下一个值,前面的值无法再取
查看下列两种写法的区别
常规写法
lis = [1,2,3]
for i in lis: # 这种常规写法,每次把可迭代对象拿进来以后,转换成迭代器对象
print(i)
for i in lis: # 把可迭代对象拿进来以后,转换成迭代器对象,每次迭代器对象都是一个新的
print(i)
# 打印结果相同,都可以出效果
使用同一个迭代器对象
lis = [1,2,3]
iter_lis = iter(lis)
for i in iter_lis: # 直接用转换好的迭代器对象
print(i)
# 正常打印
for i in iter_lis: # 第二次无法对这个迭代器对象取值,因为里面的值已经被取空
print(i)
# 无打印内容
迭代器就是迭代取值的过程
但凡可以被for循环
的都是Iterable
可迭代对象
只要能作用与next()
方法的都是Iterator
迭代器对象
Python中for循环
的本质就是不断调用next(迭代器对象)
实现的,例如
lis = ['a','b','c']
for i in lis:
print(i)
实际上等价于while循环的
lis = ['a','b','c']
iter_lis = iter(lis)
while True:
try:
print(next(iter_lis))
except StopIteration:
break
根据自身需求选择,使用方便即可
在Python中,使用了yield
关键字的函数都可以称为生成器generator
。
与普通函数不同的是生成器是一个返回迭代器的函数,简单理解来说:生成器本质上就是迭代器。
调用生成器并不会触发函数体代码的运行。我们需要通过next()
方法调用这个函数才会真正将这个函数运行起来,当运行到yield
关键字时,函数会暂停并保持当前运行的所有信息,返回yield
的值。直到下一次再调用next()
方法时,再从这个yield
下面继续向下运行。
相同点:
可以给调用者返回值
不同点:
yield可以一直进行返回值,而return只能返回一次,执行到yield函数会暂停,而执行到return函数会结束
def gen():
print('这是代码1')
yield 1
print('这是代码2')
yield 2
print(gen())
>>> <generator object gen at 0x7ffa28bf0200> # 生成器类型
print(next(gen()))
print(next(gen()))
# 奇怪的发现两次的值都是一样的,问题在于每次调用时,都是一个新的迭代器对象
# 格外注意!!!!
# 正确调用为:将拿到的迭代器对象进行固定,再对其进行调用
res = gen()
print(next(res))
> '这是代码1'
>1 # yield的返回值
# 第一次调用打印结果
# 第二次调用 从上一次yield下面开始运行
print(next(res))
> '这是代码2'
>2
# 第二次调用结果,由此看来,生成器调用时就相当于迭代器无疑
我们创建生成器基本上不会直接使用next()
方法来取里面的值,而是通过for来迭代它,并且不需要关心StopInteration
错误
def my_range(start,stop,step=1):
while start < stop:
yield start # 第一次调用返回start值,运行完yield暂停
start += step # 第二次调用,对初始值按步长增加
# 第三次就再次从头开始 yield返回 第二次调用增加的值 3
# 依次类推....
for i in my_range(1,20,2): # 每次都拿到一个yield返回值赋给i
print(i)
# 打印结果
>>> 1
>3
>5
>7
>9
>...
>17
>19
生成器产生的值并不占用多少内存,也就是说,我们可以定义这个生成器能产生多少值,但并没有立马取出来。
def my_gen(start,stop):
while True:
if start < stop:
yield start # 返回值
start += 1 # 每次在原来值上面加1
else:
break
res = my_gen(1,100000000000000000) # 拿到生成器对象,也可以理解为迭代器对象,但是生成器对象并不占用内存空间。
#
for i in res: # 不断next()生成器对象,将返回的数赋值给 i
print(i) # 注意:只是打印,并没有保存到变量。不会占用内存空间
通过简短语法做出简单的判断语句
x = 10
y = 20
# 正常写法
res = 0
if x > y:
res = x
else:
res = y
# 使用三元表达式后
res = x(条件执行成功拿到的值) if x > y else y(条件执行失败拿到的值)
> 20
上序就是最常见的三元表达式
生成式的作用就是快速拿到我们想要的值,写法类似于三元表达式。
# 原方法,对列表进行加值
lis = []
for i in range(10):
lis.append(i)
# 使用列表生成后
lis = [i for i in range(10)]
# 过滤列表内,字符串的值
name = ['jack','tom123','bom123']
#原方法
new_name = []
for i in name:
if i.endswith('123'):
new_name.append(i)
#列表生成式
new_name = [i for i in name if i.endswith('123')] # 拿到判断成功的值
> ['tom 123','bom123']
res = {
i:i for i in range(5)}
> {
0: 0, 1: 1, 2: 2, 3: 3, 4: 4}
lis = [('name','jack'),('age',18)]
res = {
k:v for k,v in lis} # 遍历出元组,通过解压赋值给k,v两个变量,再对应成字典
>{
'name': 'jack', 'age': 18}
res = {
i for i in range(10) if i %2 == 0} # 只拿到生成的偶数
> {
3, 4, 5, 6, 7, 8, 9}
res = (i for i in range(5)) # 将可生成5值的生成器赋给res
print(res)
>>> <generator object <genexpr> at 0x7ffec4cf0200>
for i in res: # 调用这个生成器,开始生成一个个数字
print(i) #
技术小白记录学习过程,有错误或不解的地方请指出,如果这篇文章对你有所帮助请
点赞 收藏+关注
子夜欢迎您的关注,谢谢支持!