Python 迭代器、生成器、三元表达式、生成式

目录

    • 迭代器
      • 迭代器的概念
      • 迭代器的作用
      • 迭代器的使用方法
      • 迭代器对象的特点
      • 迭代器对象优缺点
      • 迭代器总结
    • 生成器
      • 生成器的概念
      • yield和return
      • 生成器的定义
      • 生成器的调用方式
      • 实例:使用生成器产生数
      • 生成器的解释
    • 三元表达式
    • 生成式
      • 列表生成式
      • 字典生成式
      • 集合生成式
      • 生成器表达式


迭代器


迭代器的概念

迭代指的是一个重复过程,并非单纯的重复,可以说是在原来基础上做出的一些改动。迭代器指的就是迭代取值的过程

例如:

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只能返回一次,执行到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) #

技术小白记录学习过程,有错误或不解的地方请指出,如果这篇文章对你有所帮助请点赞 收藏+关注 子夜欢迎您的关注,谢谢支持!

你可能感兴趣的:(Python进阶,python,迭代器,生成器)