文章目录
- 1. 三元表达式
- 2. 生成式
- 2.1 列表生成式
- 2.2 字典生成式
- 2.3 集合生成式
- 2.4 元组生成式
- 3. 可迭代对象
- 4. 迭代器
- 4.1 迭代器的优缺点
- 4.2 迭代器的惰性机制
- 4.3 生成迭代器
- 4.4 文本IO包装器
- 4.5 字符串迭代器
- 4.6 列表迭代器
- 4.7 字典键迭代器
- 4.8 元组迭代器
- 4.9 集合迭代器
- 4.10 注意事项
- 4.11 模拟for循环
- 1. 执行步骤
- 2. 异常捕获
- 3. while循环遍历迭代器对象
- 4. 递归遍历迭代器对象
- 5. 生成器
- 5.1 生成器生成式
- 5.2 生成器的特点
- 5.3 yield关键字
- 1. 返回生成器
- 2. 迭代取值
- 3. yield传参
- 5.4 实例
- 6. 练习
1. 三元表达式
1.1 格式
当需求为二选一的情况下推荐使用三元表达式.
格式: 条件成立采用的值 if 条件 else 条件不成立采用的值.
值不能是关键字, ... 属于特殊的符号, 不是关键字.
1.2 示例
a = 0
b = 1
print(a if a > b else b)
def func1():
print('哈哈哈')
while True:
func1() if input('输入q退出否则继续>>>:').strip() == 'q' else ...
1.3 嵌套
三元表达式可以嵌套使用, 尽量不要去使用, Python语句注重可读性, 嵌套太复杂了.
a = 0
b = 1
c = 2
print(a if a > b else (c if c > b else a))
2. 生成式
生成式(comprehensions), 也称推导式, 作用是为容器类型快速生成元素.
生成式中for与if一起使用, 则不能搭配else, 应为else从句能跟for也能跟if这会会出现矛盾.
2.1 列表生成式
列表生成式语法: [满足条件执行的表达式 for 迭代变量 in 容器对象]
print([i for i in range(10)])
list1 = ['kid', 'qz', 'qq']
print([i + '_vip' for i in list1])
list1 = ['kid_vip', 'qz_vip', 'qq_vip']
print([i.strip('_vip') for i in list1])
list1 = ['kid_vip', 'qz_vip', 'qq_vip']
print([i.strip('_vip') for i in list1 if i == 'kid_vip'])
list1 = [lambda x: x + i for i in range(10)]
print(list1)
print(list1[0](10))
print(list1[1](10))
print(list1[2](10))
函数具有调用时才查找变量的特性, 在没调用之前它不会保存变量的具体值.
只有调用它的时候, 才逐一去找这些变量的具体值, 无所调用第几个匿名函数, 变量i已经循环完毕, 变成9了.
list1 = [lambda x, i=i: x + i for i in range(10)]
print(list1[0](10))
print(list1[1](10))
2.2 字典生成式
list1 = ['name', 'age', 'hobby']
list2 = ['kid', 18, 'red']
dic = {}
for i in range(len(list1)):
dic[list1[i]] = list2[i]
print(dic)
list1 = ['name', 'age', 'hobby']
list2 = ['kid', 18, 'red']
res = {list1[i]: list2[i] for i in range(len(list1))}
print(res)
2.3 集合生成式
list1 = ['name', 'age', 'hobby']
res = { i for i in list1}
print(res)
2.4 元组生成式
()被python中的生成器占用, 想要使用元组生成式在括号前面加上关键字tuple.
print(tuple(x for x in range(5)))
3. 可迭代对象
迭代: 迭代即更新的意思, 每次更新都必须依赖上一次的结果, 其目是为了更逼近所需结果.
可迭代对象: 即可以进行迭代操作的一类对象, 能被for循环遍历取值的对象的都是可迭代对象.
内置isinstance(Object, Iterable)函数: 判断一个对象是否是可迭代对象.
管方解释说只能识别对象含有.__iter__()方法的对象,
那么意味着对象含有.__iter__()方法都是'可迭代对象'.
一般情况下所有的双下方法都会有一个与之对应的简化写法的函数.
例: .__iter__()方法 --> iter()函数.
针对双下划线开头, 双下滑线结尾的方法标准的读法是'双下方法名', 例: 双下iter.
from collections import Iterable
str1 = ''
print(isinstance(str1, Iterable))
str1.__iter__()
4. 迭代器
迭代器: 是一种通过迭代方式取值的方式, 从序列的第⼀个元素开始访问, 直到所有的元素被访问完, 结束.
迭代器可以记住遍历对象所在的位置, 只能往后取值, 不会后退.
通常含有.__next__()方法的对象就是迭代器.
4.1 迭代器的优缺点
迭代器的优点: 节省内存, 迭代器在内存中只占一个数据的空间.
每次通过next()取值, 便会计算出一个值, 内存空间内加载当前的值, 将上一条值舍弃.
迭代器的缺点: 不能直观的查看里面的数据, 取值时不走回头路, 只能一直向下取值.
取值方式对比:
迭代取值: 是通用取值方式, 不依赖于索引取值. 特点: 永远都是往下一个取值, 无法重复获取.
索引取值: 不通用取值方式, 有序的容器类型, 才能使用. 特点: 可以重复取值.
4.2 迭代器的惰性机制
迭代器的惰性机制: next一次, 取一个值, 绝不过多取值.
迭代是数据处理的基石, 内存中放不下的数据集时,
需要一种惰性获取数据项的方式, 即按需一次获取一个数据项, 这就是迭代器模式.
4.3 生成迭代器
字符串, 列表, 字典, 元组, 集合, 文件都是可迭代对象, 通过.__iter__()方法实例化得到迭代器.
迭代取值固定语法: for element in Iterable, for语句内部通过.__next__()方法获取迭代器对象的元素.
直接使用.__next__()方法取值, 一次取出一个, 取完再取则报错:
英: StopIteration.
中: 停止迭代.
4.4 文本IO包装器
文件对象本身就是迭代器对象.
"""
a.txt内容:
1
2
3
"""
rf = open('a.txt', 'rt')
text_io_wrapper = rf.__iter__()
print(text_io_wrapper, type(text_io_wrapper))
print(text_io_wrapper.__next__())
print(rf, type(rf))
print(rf.__next__())
4.5 字符串迭代器
str_iterator = '123'.__iter__()
print(str_iterator, type(str_iterator))
print(str_iterator.__next__())
print(str_iterator.__next__())
print(str_iterator.__next__())
print(str_iterator.__next__())
4.6 列表迭代器
list_iterator = [1, 2, 3].__iter__()
print(list_iterator, type(list_iterator))
print(list_iterator.__next__())
print(list_iterator.__next__())
print(list_iterator.__next__())
4.7 字典键迭代器
dict_keyiterator = {'k1': 'v1', 'k2': 'v2'}.__iter__()
print(dict_keyiterator, type(dict_keyiterator))
print(dict_keyiterator.__next__())
print(dict_keyiterator.__next__())
4.8 元组迭代器
tuple_iterator = (1, 2, 3).__iter__()
print(tuple_iterator, type(tuple_iterator))
print(tuple_iterator.__next__())
print(tuple_iterator.__next__())
print(tuple_iterator.__next__())
4.9 集合迭代器
set_iterator = {1, 2, 3}.__iter__()
print(set_iterator, type(set_iterator))
print(set_iterator.__next__())
print(set_iterator.__next__())
print(set_iterator.__next__())
4.10 注意事项
每次执行.__iter__()方法都生成一个新的迭代器对象, 都是第一次迭代取到的值是同一个值.
str1 = 'abc'
print(str1.__iter__().__next__())
print(str1.__iter__().__next__())
print(str1.__iter__().__next__())
print(str1.__iter__().__next__())
4.11 模拟for循环
1. 执行步骤
for循环执行步骤:
* 1. in关键字后面的序列类型调用.__iter__()方法转为迭代器对象.
* 2. 循环执行.__next__()方法取值.
* 3. 取完之后再次执行.__next__()就会报错, 会自动捕获错误并处理.
2. 异常捕获
异常: 是一个事件, 该事件会在程序执行过程中发生, 影响程序的正常执行.
在解释器无法正常处理程序时就会发生异常, 程序会终止执行.
错误类型:
* 1. 语法错误: 解释器在解释源文件语句前会进行语法检测出的错误, 源代码不符合Python的编写规范.
* 2. 逻辑错误: 程序执行之后出现的错误, 语法正确, 但其代码违反逻辑规则而产生的错误, 种类很多...
异常信息三个组成部分:
* 1. 回溯信息: 最近一次错误所在行:
Traceback (most recent call last):
File "xxx", line 1, in <module>
...
* 2. 错误类型:
NameError: xxx...
* 3. 冒号后面的错误的详情原因, 解决bug的关键.
语法错误必须立刻修改, 否则程序无法执行. 出现逻辑错误可以进行针对性处理, 觉得是都让程序继续运行.
异常处理语法格式:
try:
被检测的代码.
except 错误类型 as 接收错误信息的变量:
被检测代码出错后的处理机制.
try:
name
except NameError as e:
print(e, '结束程序运行!')
Exception 是所有异常的父类, 所有异常都可以检测到.
3. while循环遍历迭代器对象
l1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55]
list_iterator = l1.__iter__()
count = len(l1)
while count:
i = list_iterator.__next__()
print(i)
count -= 1
l1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55]
list1 = l1.__iter__()
while True:
try:
print(list1.__next__())
except Exception:
break
* try语句检测程序会占用额外的资源, 执行一次就有一句try语句, 不推荐使用大量的try语句.
4. 递归遍历迭代器对象
l1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55]
list_iterator = l1.__iter__()
count = len(l1)
def func(counts):
if counts == 0:
return
i = list_iterator.__next__()
print(i)
return func(counts - 1)
func(count)
l1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55]
list1 = l1.__iter__()
def func(list1):
try:
print(list1.__next__())
except Exception:
return
return func(list1)
func(list1)
5. 生成器
有时候序列或集合的元素的非常多, 如果所有对象都直接存放在内存中, 会占用大量内存的资源.
于是设计出一种按某种顺序推导元素的算法, 需要哪个元素就迭代计算出哪个元素, 而不必创建完整的元素集合.
每次仅占用一个元素的空间, 元素被取出后, 给下一个计算结果使用, 从而节省大量的空间.
在Python中这种计算出元素的机制, 称为生成器: generator.
生成器属于自定义迭代器, 生成器保存的是计算元素的算法或表达式, 可以使用for迭代取值.
5.1 生成器生成式
Python规定生成器生成式使用()圆括号.
生成器生成式能够快速生成一个生成器, 保存了计算元素的算法.
只有在迭代取值的时候才会执行计算元素的算法, 每次仅提供一个元素.
g1 = (i for i in range(10))
print(g1)
print(next(g1))
print(next(g1))
print(next(g1))
for i in g1:
print(i, end=' ')
5.2 生成器的特点
生成器的特点, 在使用的时候才会计算, 通过一个案例来讲解:
# a.txt
123456789
123456789
123456789
with open('a.txt') as f:
g1 =(len(line) for line in f)
print(sum(g1))
为何报错?ValueError: I/O operation on closed file.
ValueError: 对已关闭文件的 IO 操作.
迭代器只有在使用的时候才执行, 元组生成式走了过场什么都什么做.
你以为代码是: (换行'
'算一个字符.)
g1 = (10, 10, 9)
其实代码是这样的:
g1 = (len(for line in f), len(for line in f), len(for line in f)
当需要执行的时候, 文件的io操作已经关闭了, 所有报错.
with open('a.txt') as f:
g1 = (len(line) for line in f)
print(sum(g1))
5.3 yield关键字
yield关键字: 在函数内使用, 调用函数时, 函数不会执行, 而是返回一个生成器.
yield的作用:
* 1. 当函数体内含有yield关键字时, 那么在调用函数的时候, 函数体代码不会执行, 而是返回一个生成器.
* 2. 可以使用yield设置返回值, 也就是生成器的元素, 多个值会组织成元组, 不写默认返回None.
* 3. 第三种传值的方式, yeied可以传递参数.
1. 返回生成器
当函数体内含有yield关键字时, 那么在调用函数的时候, 函数体代码不会执行, 而是直接返回一个生成器.
def func():
print('你好')
yield
res = func()
print(res, type(res))
2. 迭代取值
调用.__next__()方法取值时才会执行函数中的语句, 遇到yield语句返回一个值, 并保存当前函数的执行状态.
下一个次.__next__()方法则从上次暂停的位置处往回执行, 后续没有yield则执行到底.
def func():
num = 0
print(num)
yield 'yield1'
num += 1
print(num)
yield 'yield2'
num += 1
print(num)
res = func()
print(res.__next__())
print(res.__next__())
try:
res.__next__()
except StopIteration as e:
print(e)
运行工具窗口显示:
0
yield1
1
yield2
2
def func():
num = 0
print(num)
yield 'yield1'
num += 1
print(num)
yield 'yield2'
num += 1
print(num)
res = func()
for i in res:
print(i)
def func():
print(1)
yield
res = func()
for i in res:
print(i)
3. yield传参
yield传参格式:
生成器函数.send(参数1, ···), .send()方法可以让函数运行(包括yield停止的情况), 遇到yield停止.
函数中接收参数:
value=yield
程序中第一次使用生成器调用.send()方法时, 不能使用带参数的send()函数.
def my_ge():
num = yield
print(num)
res = my_ge()
print(res.send(None))
def my_ge():
num = yield
print(num)
res = my_ge()
print(res.send(1))
运行工具窗口显示:
Traceback (most recent call last):
File "C:\Users600\PycharmProjects est est.py", line 8, in <module>
print(res.send(1))
TypeError: can't send non-None value to a just-started generator
英: TypeError: can't send non-None value to a just-started generator.
中: 无法向刚启动的生成器发送非None值,
yield语句的完整执行顺序:
* 0. 调用含有yield的函数, 函数体代码不会执行, 而是返回一个生成器, 这里可以称为生成器函数.
* 1. 使用.__next__()方法时会执行函数体代码
* 2. 遇到yield语句则返回跟随它的值(双下next方法就是要这个值), 并且会暂停函数的运行保存函数的状态.
* 3. 生成器函数通过.send方法()返回一个参数给yield语句执行的结果.
* 4. 使用变量接收yield语句执行的结果.
* 5. 后续可以使用send传递的参数.
执行过yield语句后, 才能使用.send()方法传值给yield,
这个值作可以理解为yield语句执行的结果, 可以使用变量接收.
def my_ge():
while True:
print('执行my_ge函数!')
num = yield '可以传递了!'
print(num)
res = my_ge()
print(res.__next__())
res.send(1)
res.send(2)
5.4 实例
def func1():
for i in range(10):
yield i + 1 - 1 * 2 / 2
def func2():
res = func1()
for i in res:
print(i)
func2()
def my_range(start_num, end_num=None, len_num=1):
if not end_num:
end_num = start_num
start_num = 0
while start_num < end_num:
yield start_num
start_num += len_num
for i in my_range(1, 10, 2):
print(i)
def test():
for i in range(4):
yield i
g = test()
for n in [1, 10]:
g = (i + n for i in g)
"""
n = 1
g = (i + n for i in g)
n = 10
g = (i + n for i in (i + n for i in test()))
"""
res = list(g)
"""
g = (i + n for i in (i + n for i in (0, 1, 2, 3)) # n 为 10.
g = (i + 10 for i in (i + 10 for in (0, 1, 2, 3))
g = (i + 10 for i in (10, 11, 12, 13)
g = (10+10, 11+10, 12+10, 13+10
"""
print(res)
6. 练习
1. 使用递归打印斐波那契数列(前两个数的和得到第三个数, 如: 1, 2, 3, 5, 8, 13..)
def num(x, y):
if y > 100:
return
y, x = y + x, y
print(y, end=' ')
num(x, y)
num(0, 1)
2. 一个嵌套很多层的列表, 用递归取出所有的值.
list1 = [1, 2, [3, [4, 5, 6, [7, 8, [9, 10, [11, 12, 13, [14, 15]]]]]]]
def get_num(list1):
if not len(list1):
return
for i in list1:
if type(i) == int:
print(i, end=' ')
else:
get_num(i)
get_num(list1)