偷学Python第二十一天:Python中的生成式和生成器
文章目录
目标:可以用Python做一些简单的数据分析。
坚持完成自己的目标!勿忘初心~
今日学习目标
学习列表生成式,字典生成式、集合生成式
今日学习内容
列表生成式
语法格式
# 普通的生成式
[表达式 for 变量 in 旧列表]
# 加条件的生成式
[表达式 for 变量 in 旧列表 if 条件]
# if...else条件的生成式
[表达式 if 条件1 else 条件2for 变量 in 旧列表]
甜甜小案例
- 过滤掉名字字母不足4的名字。示例代码↓
"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/20
"""
names = ["Tom", "Lily", "Jack", "Steven", "Bod"]
# 在不是用列表生成式的过程
news_names = []
for name in names:
if len(name) > 3:
news_names.append(name)
print(news_names) # ['Lily', 'Jack', 'Steven']
# 使用列表生成式
new_names = [name for name in names if len(name) > 3]
print(new_names) # ['Lily', 'Jack', 'Steven']
显而易见,使用列表生成式可以节省很多行代码
- 将1-100的整数可以被3和5整除的数字,组成一个新的列表.示例代码↓
# 将1-100的整数可以被3和5整除的数字,组成一个新的列表
# 旧方法
number_list = []
for i in range(101):
if i % 3 == 0 and i % 5 == 0:
number_list.append(i)
print(number_list) # [0, 15, 30, 45, 60, 75, 90]
# 列表生成式
new_num_list = [i for i in range(101) if i % 3 == 0 and i % 5 == 0]
print(new_num_list) # [0, 15, 30, 45, 60, 75, 90]
- 将0到10的奇数和0到5的偶数组成一个不重复的列表,示例代码
# 将0到10的奇数和0到5的偶数组成一个不重复的列表
# 旧方法
news_number_list = []
for x in range(10):
if x % 2 != 0:
for y in range(5):
if y % 2 == 0:
news_number_list.append([x, y])
print(news_number_list)
# [[1, 0], [1, 2], [1, 4], [3, 0], [3, 2], [3, 4], [5, 0], [5, 2], [5, 4], [7, 0], [7, 2], [7, 4], [9, 0], [9, 2],[9, 4]]
# 新方法
news_num_list = [[x, y] for x in range(10) if x % 2 != 0 for y in range(6) if y % 2 == 0]
print(news_num_list)
# [[1, 0], [1, 2], [1, 4], [3, 0], [3, 2], [3, 4], [5, 0], [5, 2], [5, 4], [7, 0], [7, 2], [7, 4], [9, 0], [9, 2],[9, 4]]
同样支持多个for语句,如果使用原来的那种方法的话层级结构太深了,使用生成式一行解决
4.将列表中的数字大于8000的加200 小于等于8000的加500
# 将列表中的数字大于8000的加200 小于等于8000的加500
number = [5000, 10000, 4500, 80000, 12000]
# 旧方法
for i in number:
if i > 8000:
i += 200
else:
i += 500
print(number) # [5000, 10000, 4500, 80000, 12000]
# 新方法
new_number = [i + 200 if i > 8000 else i + 500 for i in number]
print(new_number) # [5000, 10000, 4500, 80000, 12000]
集合生成式
语法结构
# 普通的生成式
{表达式 for 变量 in 旧列表}
# 加条件的生成式
{表达式 for 变量 in 旧列表 if 条件}
# if...else条件的生成式
{表达式 if 条件1 else 条件2for 变量 in 旧列表}
语法结构与列表生成式基本相同,但是因为集合不允许有重复,所有得出的结果自动去重
字典生成式
字典生成式与集合生成式和列表生成式的语法结构一致,唯一不同的是字典是以键值对的形式存放信息的
甜甜小案例
将dict1中的键值对互换
"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/20
"""
# 将字典的键值对互换
dict1 = {"a": "A", "b": "B", "c": "C"}
# 就方法
new_dict1 = {}
for key, value in dict1.items(): # 返回一个存放键值对的元组
new_dict1[value] = key
print(new_dict1) # {'A': 'a', 'B': 'b', 'C': 'c'}
# 新方法
news_dict1 = {value: key for key,value in dict1.items()}
print(news_dict1) # {'A': 'a', 'B': 'b', 'C': 'c'}
item
方法:返回可遍历的(键, 值) 元组数组。注意:返回值是以键值对的方式存放的
生成器(Generator)
现在可以通过生成器来直接创建一个列表,但是由于内存的限制,列表的容量肯定是有限的,如果我们需要一个包含几百个元素的列表,但是每次访问的时候只访问其中的几个,那剩下的元素不使用就很浪费内存空间。
这个时候生成器(Generator)就起到了作用,他是按照某种算法不断生成新的数据,直到满足某一个指定的条件结束
得到生成式的方式:
-
通过列表生成式来得到生成式
""" -*- coding:uft-8 -*- author: 小甜 time:2020/5/20 """ g = (x for x in range(10)) # 将列表生成列的[]改变成为() # 打印其类型 print(type(g)) # <class 'generator'> # 调用其元素 print(g.__next__()) # 0 print(g.__next__()) # 1 print(g.__next__()) # 2 print(g.__next__()) # 3 print(g.__next__()) # 4 # 使用.__next__的方式调用 print(next(g)) # 5 print(next(g)) # 6 print(next(g)) # 7 print(next(g)) # 8 print(next(g)) # 9 # 使用next()的方法调用 print(next(g)) # 当数据调用不到时会报出错误 StopIteration
需要多少调用多少,不调用的不会生成,也就不会占用内存空间,可以使用循环结构来按照需要来调用
""" -*- coding:uft-8 -*- author: 小甜 time:2020/5/20 """ g = (x for x in range(10)) # 将列表生成列的[]改变成为() skip = True # 判断条件 count = 0 # 调用次数 while skip: count += 1 # 循环一次+1 print(next(g)) if count > 9: break # 跳出循环
-
使用函数借助
yield
关键字来完成生成斐波那契数列的前20个数。
说明:斐波那契数列(Fibonacci sequence),又称黄金分割数列,是意大利数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)在《计算之书》中提出一个在理想假设条件下兔子成长率的问题而引入的数列,所以这个数列也被戏称为"兔子数列"。斐波那契数列的特点是数列的前两个数都是1,从第三个数开始,每个数都是它前面两个数的和,形如:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …。斐波那契数列在现代物理、准晶体结构、化学等领域都有直接的应用。
""" -*- coding:uft-8 -*- author: 小甜 time:2020/5/20 """ a = 0 b = 1 for _ in range(20): # 循环20次 a, b = b, a + b # 同时进行赋值 print(a, end=' ')
这是以前完成的效果,现在将其改造为生成器的函数
""" -*- coding:uft-8 -*- author: 小甜 time:2020/5/20 """ def fun(length): a, b = 0, 1 for _ in range(length): a, b = b, a + b yield a fib = fun(20) print(type(fib)) # <class 'generator'> # 打印类型 count = 0 while count < 20: print(next(fib), "", end="") # 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 count += 1
使用生成列就可以很优雅的利用内存,大可能的不浪费内存
完成流程
在执行过程中,遇到yield关键字就会暂停执行,下次调用则继续从上次暂停的位置继续执行,因为是一个循环语句,所有会直接跳到for语句
如果在调用yield,需要给它传值,就要使用
.send()
方法了。示例代码""" -*- coding:uft-8 -*- author: 小甜 time:2020/5/20 """ def fun(num): n = 0 for i in range(num + 1): n += i ret = yield n print(f"这是+到{ret}的第{i + 1} 次") g = fun(3) print(g.send(None)) print(g.send('3')) print(g.send('3')) print(g.send('3')) ''' ---输出结果--- 0 这是+到 3 的第 1 次 1 这是+到 3 的第 2 次 3 这是+到 3 的第 3 次 6 '''
send的加入可以使生成器更加灵活,但是需要注意的是第一次调用生成器的send()方法时,参数只能为None,否则会抛出异常。当然也可以在调用send()方法之前先调用一次next()方法,目的是让生成器先进入yield表达式。
迭代器与可迭代的生成器
可迭代的对象有生成器、元组、列表、集合、字典和字符串等
通过collections
的Iterable
函数结合isinstance(object, classinfo)
来判断一个对象时不是可迭代的对象
迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。很生成器也是迭代器。
可以被next ()
函数调用并不断返回下一个值的对象称为迭代器: Iterator
,可以使用isinstance()
判断一个对象是否是Iterator
对象:
注意:可迭代的不一定是生成器,但是生成器一定第可迭代的。
把元组、列表、集合、字典和字符串等Iterable
变成Iterator
可以使用iter()
函数
Iterable
和Iterator
的区别
Iterable
是可以作为for
循环对象的统称;而Iterator
对象需要被next()
函数调用才不断返回下一个数据,直到没有数据时抛出StopIteration
错误,而在这之前是不会知道其长度的,所以Iterator
的计算是惰性的,只有next()
函数叫他才会返回结果,Iterator
甚至可以表示一个无限大的数据流,例如全体自然数。
示例代码
"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/20
"""
from collections.abc import Iterable, Iterator
a = [1, 2, 3]
b = {1, 2, 3}
c = (1, 2, 3)
d = "123"
e = 123
f = (x for x in range(5))
# 打印数据类型
print(type(a)) # <class 'list'>
print(type(b)) # <class 'set'>
print(type(c)) # <class 'tuple'>
print(type(d)) # <class 'str'>
print(type(e)) # <class 'int'>
print(type(f)) #<class 'generator'>
print("-" * 20)
# 打印是否为可迭代对象
print(isinstance(a, Iterable)) # True
print(isinstance(b, Iterable)) # True
print(isinstance(c, Iterable)) # True
print(isinstance(d, Iterable)) # True
print(isinstance(e, Iterable)) # False
print(isinstance(f, Iterable)) # True
print("-" * 20)
# 除了字符串都是可迭代对象
# 打印是否是迭代器
print(isinstance(a, Iterator)) # False
print(isinstance(b, Iterator)) # False
print(isinstance(c, Iterator)) # False
print(isinstance(d, Iterator)) # False
print(isinstance(f, Iterator)) # True
# 只有f(生成器)是迭代器
print("-" * 20)
# 通过iter()将可迭代转换为迭代器
print(isinstance(iter(a), Iterator)) # True
print(isinstance(iter(b), Iterator)) # True
print(isinstance(iter(c), Iterator)) # True
print(isinstance(iter(d), Iterator)) # True
今日学习总结
了解了列表生成式,字典生成式、集合生成式的语法结构,值得注意的是嵌套else
语句的时候需要提前的for
语句前面;学习了生成器的两种生成语法:直接将列表生产式的括号修改为圆括号或者使用函数借助yield
关键字来完成;可以通过.__next__()
、next()
函数、.send()
方法来调出数据
知道了什么是可迭代对象、迭代器两者的区别;可以通过isinstance(object, Iterable)
来判断是不是一个可迭代的对象、通过isinstance(object, Iterator)
来判断是不是一个迭代器,这两个方法都来自collections.abc
库;可以通过iter()
函数将可迭代对象转变为迭代器
明日学习计划
学习函数的闭包和装饰器
甜甜有话说
一开始过的太快了,很多知识都没有学到,敲得代码读的代码太少了,直接做案例太不容易了,昨天憋了一天也没有搞明白,以前的基础太差了,现在开始把前面漏掉的知识点捡起来,重新学一学,为以后打基础。
加油~ !
座右铭:不要在该奋斗的年纪选择安逸!!
古之立大事者,不惟有超世之才,亦必有坚忍不拔之志。——苏轼