Python基础(十一)
今日主要内容
- 补充:三目运算
- f-strings
- 迭代器
- 生成器
补充:三目运算
- 三目运算(三元运算)结构:
表达式1 if 条件表达式 else 表达式2
c = a if a > b else b
-
执行流程:
- 判断条件,如果条件正确将a赋值给c
- 如果条件不正确将b赋值给c
a = 10 b = 20 c = a if a > b else b # 三目运算 print(c) 运行结果: 20
一、f-strings
- f-strings之前我们已经说过了,python3.6以上的版本可以使用,用来格式化输出,非常的方便,今天来详细说一下
-
f-strings的格式:
f"xxxx{传入的变量}xxxx"
(建议使用f
)F"xxxx{传入的变量}xxxx"
(不建议使用F
)
name = "zxd" age = 23 print(f"姓名:{name} 年龄:{age}") 运行结果: 姓名:zxd 年龄:23
-
引号中如果需要大括号
{}
时,用两个{{}}
代表print(f"{{'a'}}") # 用两个大括号表示 运行结果: {'a'}
-
引号中需要使用引号时,一定用两个单引号
''
表示print(f"{{'a'}}") # 用单引号表示 运行结果: {'a'}
-
传入的参数可以是三目表达式
a = 10 b = 10 print(f"{a if a > b else b}") 运行结果: 10
二、迭代器
(一)可迭代对象
-
说迭代器之前咱们来看一看可迭代对象,什么是可迭代对象?
- 可以一个一个取值的对象就是可迭代对象
s = "12345" lst = [1, 2, 3, 4, 5] dic = {1: 1, 2: 2, 3: 3} ....... # 这些都是可迭代对象
- 他们共有的一个特点就是可以被for循环
s = "12345" lst = [1, 2, 3, 4, 5] dic = {1: 1, 2: 2, 3: 3} for el in s: print(el) for el in lst: print(el) for el in dic: print(el)
-
查看可迭代对象的官方方法:
- 查看对象是否有
__iter__()
方法,只要使用有此方法的对象全部都是可迭代对象 dir()
函数可以查看对象所有的方法
lst = [1, 2, 3, 4, 5] print("__iter__" in dir(lst)) 运行结果: True
- 查看对象是否有
-
可迭代对象的特点:
- 空间换时间的理念(用大量的空间节省时间)
- 优点:
- 使用灵活,每个可迭代对象都有自己的方法
- 能够直接查看元素个数
- 可以重复取值
- 缺点:
- 占内存
-
应用:内存空间大,当数据量比较少,建议使用可迭代对象
(二)迭代器
- 迭代器可以理解成可迭代对象的实体化,它只继承了迭代性(可以一个一个取值),同时节约了内存(唯一的优点)
- 文件句柄就是一个迭代器
-
迭代器的生成方法:
- 两种生成方法效果相同
iter(可迭代对象)
- 生成可迭代对象的迭代器
- 打印的是迭代器的地址
lst = [1, 2, 3, 4, 5] l = iter(lst) print(l) 运行结果: <list_iterator object at 0x0000020BAFCEA940>
可迭代对象.__iter__()
- 生成可迭代对象的迭代器
- 打印的是迭代器的地址
lst = [1, 2, 3, 4, 5] l = lst.__iter__() print(l) 运行结果: <list_iterator object at 0x000002490EFDA8D0>
-
迭代器的取值
- 迭代器最大的特点就是惰性机制,如果不主动向迭代器取值,迭代器是不会给你值的,同时也正因为惰性机制节约了内存
next(迭代器)
lst = [1, 2, 3, 4, 5] l = iter(lst) print(next(l)) print(next(l)) print(next(l)) print(next(l)) print(next(l)) 运行结果: 1 2 3 4 5
迭代器.__next__()
lst = [1, 2, 3, 4, 5] l = lst.__iter__() print(l.__next__()) print(l.__next__()) print(l.__next__()) print(l.__next__()) print(l.__next__()) 运行结果: 1 2 3 4 5
- 每次执行取值函数只向迭代器取一个值,按顺序向下取值,不能重复取值,迭代器中有多少个元素就只能next多少次,超出最大个数会报错
lst = [1, 2, 3, 4, 5] l = lst.__iter__() print(l.__next__()) print(l.__next__()) print(l.__next__()) print(l.__next__()) print(l.__next__()) print(l.__next__()) 运行结果: 1 2 3 4 5 StopIteration
-
迭代器的特点:
- 时间换空间理念(用大量的时间去节省空间)
- 节省内存
- 惰性机制
- 只能向下取值,不能往复
-
for循环的本质就是一个迭代器
- 捕获异常:向迭代器取值超出迭代器元素数量时,会捕获StopIteration异常,从而终止while循环
lst = [1, 2, 3, 4, 5] l = iter(lst) while True: try: print(next(l)) except StopIteration: # 捕获异常 break
-
向同一个迭代器取值,迭代器内部会记录取值位置,赋值给变量,变量会指向地址和上次取值位置
lst = [1, 2, 3, 4, 5] l_iter = iter(lst) print(next(l_iter)) print(next(l_iter)) print(next(l_iter)) print(next(l_iter)) print(next(l_iter)) # l_iter指向取值记录位置 运行结果: 1 2 3 4 5
l_iter
指向迭代器的地址,每一次取值,l_iter
指向上一次取值位置- 可以理解成通过熟人买东西,每次买都是上次的优惠价
-
向同一个迭代器取值,迭代器内部会记录取值位置,若不赋值,每一次取值都从开头取值,相当于每次寻址后都从头开始
lst = [1, 2, 3, 4, 5] print(next(iter(lst))) print(next(iter(lst))) print(next(iter(lst))) print(next(iter(lst))) print(next(iter(lst))) print(next(iter(lst))) # 每次都从头开始取值 运行结果: 1 1 1 1 1
- 没有赋值每次通过
func()
直接寻址,都从头部开始取值 - 可以理解成没有熟人了,每次买东西都是原价
- 没有赋值每次通过
-
应用:内存小,数据量巨大时,建议使用迭代器
(三)两者关系
- 迭代器一定是可迭代对象,可迭代对象不一定是迭代器
- 迭代器可以通过
iter(可迭代对象)
和可迭代对象.__iter__()
得到
三、生成器
(一)什么是生成器
-
生成器的本质就是迭代器
-
生成器就是一个自己写的迭代器,而迭代器只能通过
iter()
函数得到 -
生成器的目的是不通过数据转换实现,通过代码实现
- 列表转换成了迭代器,但是列表依旧加载到了内存,没有达到省内存的效果
lst = [1, 2, 3, 4, 5] l_iter = iter(lst) print(next(l_iter)) print(next(l_iter)) print(next(l_iter)) print(next(l_iter)) print(next(l_iter)) 运行结果: 1 2 3 4 5
- 通过生成器真正达到省内存的效果
def func(): yield 1 yield 2 yield 3 yield 4 yield 5 f_gen = func() print(next(f_gen)) print(next(f_gen)) print(next(f_gen)) print(next(f_gen)) print(next(f_gen)) 运行结果: 1 2 3 4 5
(二)生成器
-
通过函数实现生成器
- 先来看一个函数
def func(): print(1) return 1 print(func()) 运行结果: 1 1
- 将函数中
return
替换成yield
就变成了一个生成器
def func(): print(1) yield 1 print(func()) 运行结果: <generator object func at 0x000001B27042C50>
- 如果定义的是函数,函数名加括号是调用函数;而如果定义的是生成器,函数名加括号是得到的是生成器的内存地址
yield
:- yield能返回多个值,以元组形式存储
- yield能返回各种数据类型
- yield能够写多个并且都能执行
- yield能够记录执行位置
- yield后面不写内容,默认返回None
- yield只能向下进行,不能往复,一次性取值
-
生成器的取值
next(生成器)
def func(): yield 1 yield 2 yield 3 yield 4 yield 5 f_gen = func() print(next(f_gen)) print(next(f_gen)) print(next(f_gen)) print(next(f_gen)) print(next(f_gen)) 运行结果: 1 2 3 4 5
生成器.__next__()
def func(): yield 1 yield 2 yield 3 yield 4 yield 5 print(func().__next__()) print(func().__next__()) print(func().__next__()) print(func().__next__()) print(func().__next__()) 运行结果: 1 2 3 4 5
-
生成器的本质就是一个迭代器,所以它拥有迭代器的所有特点
- 时间换空间理念(用大量的时间去节省空间)
- 节省内存
- 惰性机制
- 只能向下取值,不能往复
-
向同一个生成器取值,yield会记录取值位置,赋值给变量,变量会指向地址和上次取值位置
def func(): yield 1 yield 2 yield 3 yield 4 yield 5 f_gen = func() print(next(f_gen)) print(next(f_gen)) print(next(f_gen)) print(next(f_gen)) print(next(f_gen)) # f_gen指向生成器地址和yiele记录的位置 运行结果: 1 2 3 4 5
-
向同一个生成器取值,yield会记录取值位置,若不赋值,每一次取值都从开头取值,相当于每次寻址后都从头开始
def func(): yield 1 yield 2 yield 3 yield 4 yield 5 print(next(func())) print(next(func())) print(next(func())) print(next(func())) print(next(func())) # 每次都从头开始取值 运行结果: 1 2 3 4 5
-
若yield的值是个可迭代对象,还可以将其对象逐个返回
yield from
def func(): yield from [1, 2, 3] yield from [4, 5, 6] print(next(func())) print(next(func())) print(next(func())) print(next(func())) print(next(func())) print(next(func())) 运行结果: 1 2 3 4 5 6
四、三者区分
(一)可迭代对象
- 只要是可以使用
__iter__()
方法的对象都是可迭代对象 - 迭代器和生成器都是可迭代对象
(二)迭代器
- 查看对象的内存地址,如果有
iterator
就是一个迭代器 - 拥有
__iter__()
和__next__()
放法的就是一个迭代器
(三)生成器
- 查看对象的内存地址,如果有
generator
就是一个生成器 - 可以使用
send()
方法的就是一个生成器