Python基础第九天——迭代对象、 迭代器对象、生成器、三元表达式列表解析、生成器表达式
鸡汤:
要时刻不断地给自己灌输一种思想:都TMD是个人,凭什么他会而我就不会?王候将相宁有种乎?我承认人有天赋的差别,但是勤奋能弥补缺陷!所以,根据以上观点得出以下结论,只要出生时不是个傻子,那么就没有蠢的人,只有懒的猪!只要勤奋上进,小白也会变大神。加油
——奔跑吧小白
一、迭代对象、迭代器对象
1、迭代
定义:带有__iter__方法的就是可迭代对象
python常用的数据类型中,除了数字外,都是迭代对象。
例:用isinstance判断python常用数据类型是否为迭代对象,经验证,python常用数据类型中除数字类型外,都是可迭代对象。
from collections import Iterable int1 = 1 str1 = 'xiaobai' list1 = [1,2,3,4,5] tuple1 = (1,2,3,4,5) dic1 = {'a':1,'b':2,'c':3} set1 = {1,2,3,4} f = open('a.txt','w') # 用isinstance判断是否为迭代对象 print(isinstance(int1,Iterable)) # 数字类型结果:False print(isinstance(str1,Iterable)) # 字符串结果:True print(isinstance(list1,Iterable)) # 列表结果:True print(isinstance(tuple1,Iterable)) # 元组结果:True print(isinstance(dic1,Iterable)) # 字典结果:True print(isinstance(set1,Iterable)) # 集合结果:True print(isinstance(f,Iterable)) # 文件结果:True
输出结果:
False
True
True
True
True
True
True
2、迭代器
定义:有__iter__和__next__的方法的就是迭代器。
取值:通过__next__方法,加上括号拿到迭代器里的值。(迭代器也有一个__iter__方法。)
特点:
优点:不依赖于索引,惰性计算节省内存
缺点:取值不方便,一次性取值,只能住后取,不能回退。
例1:迭代器用__next__()的方式取值
l = [1,2,3,4,5] x = l.__iter__() # 迭代器 print(x.__next__()) print(x.__next__()) print(x.__next__()) print(x.__next__()) print(x.__next__()) print(x.__next__()) # 已知列表l有5个值, # 当取到第6个值的时候会异常 StopIteration
输出结果:
1 Traceback (most recent call last): 2 File "C:/Users/William/PycharmProjects/Python_Item2/study/day9/迭代器.py", line 19, in <module> 3 4 print(x.__next__()) # 已知列表l有5个值, 5 StopIteration
例2:用isinstance判断python常用数据类型是否为迭代器对象
from collections import Iterator int1 = 1 str1 = 'xiaobai' list1 = [1,2,3,4,5] tuple1 = (1,2,3,4,5) dic1 = {'a':1,'b':2,'c':3} set1 = {1,2,3,4} f = open('a.txt','w') # 用isinstance判断是否为迭代器对象 print(isinstance(int1,Iterator)) # 数字类型结果:False print(isinstance(str1,Iterator)) # 字符串结果:True print(isinstance(list1,Iterator)) # 列表结果:True print(isinstance(tuple1,Iterator)) # 元组结果:True print(isinstance(dic1,Iterator)) # 字典结果:True print(isinstance(set1,Iterator)) # 集合结果:True print(isinstance(f,Iterator)) # 文件结果:True
输出结果:
False
False
False
False
False
False
True
例3:迭代器执行__iter__()方法后得到的还是自己。
# 文件是迭代器 with open('a.txt','r',encoding='utf-8') as f: print(f) # 得到迭代器 obj = f.__iter__() # 执行__iter__后仍然是迭代器 print(obj is f) # True
输出结果:
<_io.TextIOWrapper name='a.txt' mode='r' encoding='utf-8'> True
3、for循环原理
步骤:
1、先执行迭代对象的__iter__()方法,得到迭代器
2、for循环会自动调用迭代器内的__next__()方法,将得到的返回值再赋予变量
3、直到捕捉到StopIteration这个异常后,再结束循环。
for循环只能判断要遍历的对象是否为迭代对象,而不能只判断是否为迭代器。
例:
l = [1,2,3,4] for i in l: # 1、先执行迭代对象的__iter__()方法,得到迭代器 # 2、for循环会自动调用迭代器下面的__next__()方法,得到返回值后再赋予变量“i” # 3、直到报出StopIteration异常,for循环再捕捉到这个异常,最后结束循环。 print(i)
补充:
要循环一个文件里的内容,不需要用f.readlines()把文件所有的内容都取出来,这样做是把文件的内容全都读到内存中了。我们只需通过迭代器的形式——“for i in 文件名”的形式取出文件里的内容,这样不会占用太大的内存。
4、用while循环实现不依赖于索引的方式取值。
(1)try...except
这里要用到一个新的知识点:
try...except
用于捕捉程序出现的异常。
语法:
try
逻辑
except 要捕捉的异常:
条件
(2)用while循环实现不依赖于索引的方式取值
d = {'a':1,'b':2,'c':3} obj = d.__iter__() # 1、先将迭代对象转换成迭代器 while True: # 2、再用__next__()方法取值 try: # 3、用try...except捕捉StopIteration的异常。 i = obj.__next__() print(i) except StopIteration: break
二、生成器
1、定义:
生成器也叫生成器函数,函数体内包含yield关键字,那么该函数执行的结果是生成器函数。
生成器也是迭代器。因为它下面也有__iter__方法和__next__方法。
例1:证明生成器就是迭代器
def foo(): print('start') yield print('end') g = foo() # 调用时并不会执行函数内的代码,它会得到一个生成器。把它赋值给变量g print(g) # 得到生成器 # 通过以下方法证明生成器就是迭代器 from collections import Iterator print(isinstance(g,Iterator)) # 结果为True
2、生成器的取值
生成器的取值是通过“__next__()”的方式取值的。
例:
def foo(): print('start') yield print('end') g = foo() # 得到生成器,生成器就是迭代器 g.__next__() # 结果为start # 重点:__next__()一次,则触发函数的一次执行,因为g = foo()
输出结果:
start
3、yied的特性
函数一定要有return才能结束,即使不给函数加return,函数默认也有return None,
所以说函数要执行一次,就会从头到尾依次执行函数体,直到碰到return函数才会停止。
yield功能:
1、与return特性一样,充当函数的返回值。与return不同之处在于yield可以返回多次值,而return只能返回一次函数就结束了
2、把函数封装好了__iter__和__next__方法,将函数的执行结果做成了一个迭代器。
3、遵循迭代器的取值方式。例:obj.__next__(),触发函数的执行,函数暂停与再继续的状态都是由yield保存起来的
例:
def foo(): print('start') yield # yield功能与return相似, # 函数运行时碰到yield时函数结束,并用来返回值。yield后面不跟值默认是返回None # 所以第一次g.__next__()返回值就是None,用print打印查看 print('end') g = foo() # 得到生成器,生成器就是迭代器 # g.__next__() # 结果为start,重点:__next__()一次,则触发foo函数的一次执行。 res = g.__next__() print(res) # 结果为None,因为yield后面没有跟任何参数
输出结果:
start
None
4、yield与return的区别
(1)return在一个函数中可以有多个,但是只能执行一次值
def foo(): print('---------first---------') return 1 print('---------second--------') return 2 print('---------third---------') return 3 foo() # 一个函数中有多个return时,只会执行一个return,只能执行一次值。
输出结果:
---------first---------
(2)yield在一个函数中可以有多个,并且能执行多次值
当一个函数中有多个yield时,用“__next__()”方法执行一次函数后,函数就暂停住了,再次执行函数时,函数则会接上一次暂停的位置接着往后走。(这就把函数与迭代器的概念结合一块了。)
例:
def foo(): print('---------first---------') yield 1 print('---------second--------') yield 2 print('---------third---------') yield 3 print('---------fourth---------') g = foo() print(g.__next__()) # 执行第一次函数,得到返回值:1 print(g.__next__())# 接上次暂停的的位置继续执行,得到返回值:2 print(g.__next__())# 接上次暂停的的位置继续执行,得到返回值:3 print(g.__next__())# 接上次暂停的的位置继续执行,但是没有找到yield, # 此时__next__()一直在找返回值,但是返回值是由yield控制的。 # yield与return不同,不写yield它不会默认返回None,它则会报错StopIteration的错误
输出结果:
---------first--------- Traceback (most recent call last): 1 File "C:/Users/William/PycharmProjects/Python_Item2/study/day9/生成器可以被for循环遍历.py", line 22, in <module> ---------second-------- print(g.__next__()) 2 StopIteration ---------third--------- 3 ---------fourth---------
(3)生成器可以被for循环遍历。
因为通过yield把函数做成了迭代器。迭代器调__iter__()的结果还是其本身。
例1:
def foo(): print('---------first---------') yield 1 print('---------second--------') yield 2 print('---------third---------') yield 3 print('---------fourth---------') g = foo() for i in g: # 虽然循环体内写的pass,但是使用for循环相当于在调__iter__()方法,(for循环的特性) pass # 生成器就是迭代器 # 迭代器在调用__iter__()时得到的还是它自己 # 当走到函数最后一条不带yield时,for循环自带了捕捉异常的功能。所以不会报错
输出结果:
---------first--------- ---------second-------- ---------third--------- ---------fourth---------
5、计数功能
def countdown(n): print('start countdown') while n > 0: yield n n -= 1 print('stop countdown') g = countdown(5) # print(g.__next__()) # 5 # print(g.__next__()) # 4 # print(g.__next__()) # 3 # print(g.__next__()) # 2 # print(g.__next__()) # 1 # print(g.__next__()) # 报StopIteration错误。 # 生成器又是迭代器,所以可以用for循环来取值。 for i in g: # 用for循环自动捕捉异常 print(i)
输出结果:
start countdown 5 4 3 2 1 stop countdown
6、用代码模拟linux命令:tail -f 功能
tail -f 功能:动态查看文件新增的内容。
思路:
首先以读的方式打开一个文件,将光标移至文件内容的最后位置。
然后写一个循环读的过程,有两种情况:一种是读到有值,另一种是没有读到值
最后读到值时则返回,没有读到值则进入下一次循环
代码如下:
# tail -f 的功能是动态查看文件新增一行的内容。 import time def tail(file_path,encoding='utf-8'): # 1、传一个文件路径进来,并指定默认形参(字符编码默认是utf-8) with open(file_path,'r',encoding=encoding) as f: f.seek(0,2) # 2、让光标从文件内容的末尾开始, while True: # 3、循环的读 line = f.readline() # 4、再开始读就是读新增的内容了。 if line: # 5、判断读的内容是否存在,如果为真,表示文件新增了内容,然后打印内容 print(line,end='') else: # 5、如果读不到内容,表示未新增内容,则让程序睡0.5秒,此时程序是死循环,加上时间是为了不让程序执行过快而占用内存。 time.sleep(0.5) tail('a.txt')
先运行以上代码,然后打开一个a.txt文件,在a.txt文件的最后一行输入内容,查看效果。
运行效果:
往a.txt文件里添加以下两行内容,然后保存
再查看代码,此时已经实现了tail -f 命令的功能
7、用代码模拟linux命令:tail -f | grep '要过滤的内容'
(1)先用代码实现grep命令的功能
能过以上第6小节已了解tail -f 是动态查看文件新增内容的功能,而grep则是过虑指定内容的功能。
思路:
在linux命令中,用“tail -f | grep '要过滤的内容'”一条命令就可以实现功能,但是我们在模拟这一条命令的时候,不能把这一条命令的所有功能都写在一个函数内,必须分为两个函数,一个函数写动态查看命令功能,另一个函数写过滤功能。所以,以下代码先完成过滤的功能。
传两个值,例如lines和patter,lines表示文件中的多行内容,patter表示要过滤的内容,然后循环每一行内容,判断patter是否在lines内,如果是则打印该行内容。
在python中不管一个功能的作用有多大或多小,都必须要单独写一个函数进行封装。
代码如下:
# 过滤功能 def grep(lines,pattern): # 接收文件中一行行的内容 ,lines表示文件中的多行内容,pattern表示要过滤的内容 for line in lines: if patter in line:# 判断要过滤的内容是否在文件中,如果是则打印包含要过滤的内容的这行来。 print(line) grep(lines,'要过滤的内容') # 传入两个值,一个是文件内容,另一个则是要过滤的内容。
(2)用代码实现linux命令完整功能——tail -f | grep '要过滤的内容'的功能'
但是以上过滤功能的代码在调用时需要传的lines,而lines则必须要先执行tail -f这个功能后,才能得到源源不断的、新增的文件内容。所以此时就要用到生成器功能来实现这条完整的linux命令。
代码如下:
import time # 定义阶段 # 模拟动态查看文件功能:tail -f def tail(file_path,encoding='utf-8'): with open(file_path,'r',encoding=encoding) as f: f.seek(0,2) while True: line = f.readline() if line: yield line # 表示有内容时,用yield暂停住 else: time.sleep(0.5) # 模拟过滤功能:grep '要过滤的内容' def grep(lines,pattern): for line in lines: # 遍历生成器给值的每一行进行判断 if pattern in line: print(line) # 调用阶段 g = tail('a.txt') # 将调用函数的结果赋予变量g,此时变量g为生成器, # 要取值则用g.__next__(),也可以用for循环取值,因为for会自动触发__next__() grep(g,'error') # 将生成器和要过滤的内容传给grep函数
运行效果:
先打开a.txt文件,内容如下:
运行以上代码后,再往.a.txt文件中添加以下内容并保存:
此时,得到过滤字符‘error’后的行,内容如下:
8、用代码模拟linux命令:tail -f | grep '要过滤的内容' | grep '想要过滤的内容' | grep '想要过滤的内容'.....
说明:本小节意思为linux在使用过滤命令时后面可能要接多个过滤的内容。所以在第7小节的例子中不能将第一次过滤的内容打印,而是将它变成生成器。
例:
要求:用代码模拟linux命令:tail -f | grep 'error' | grep '404' | grep 'xiaobai',表示过滤出动态文件中包含有‘error’、'404'、'xiaobai'的一行。
代码如下:
# 要求:用代码模拟linux命令:tail -f | grep 'error' | grep '404'|grep 'xiaobai' import time # 定义阶段 # 模拟动态查看文件功能:tail -f def tail(file_path,encoding='utf-8'): with open(file_path,'r',encoding=encoding) as f: f.seek(0,2) while True: line = f.readline() if line: yield line # 表示有内容时,用yield暂停住 else: time.sleep(0.5) # 模拟过滤功能:grep '要过滤的内容' def grep(lines,pattern): for line in lines: if pattern in line: yield line # 将grep函数变成生成器,以便于向外传多个值。 # 调用阶段 tail_g = tail('a.txt') # tail函数的生成器 grep_g1 = grep(tail_g,'error') # grep_g2 = grep(grep_g1,'404') # grep_g3 = grep(grep_g2,'xiaobai') for line in grep_g3: print(line)
运行效果:
先打开a.txt文件,内容如下:
运行以上代码后,再往.a.txt文件中添加以下内容并保存:
最终得到过滤出"error"、"404"、"xiaobai"的行:
三、三元表达式,列表解析,生成器表达式
1、三元表达式
定义:一共有三个元素:一个条件,条件成立的情况下产生的值,条件不成立的情况下产生的值
格式:
例:比较x和y中最大的值。
res =x if x > y else y # 根据条件情况,将值往左右两边放
print(res)
之前我们用if判断定义一个三元表达式的代码如下:
例1:输入两个值通过以下代码进行比较并出最大的值
while True: user_input = input('Welcome to you!Do you want play? yes/no:').strip() if user_input == 'yes': x = input('X:').strip() y = input('Y:').strip() if x.isdigit() and y.isdigit(): x = int(x) y = int(y) if x > y: print('The bigger number is:%s,so \033[42mX\033[0m is the biggest' % x) else: print('The bigger number is:%s,so \033[42mY\033[0m is the biggest' % y) else: print('Bitch,what the fuck are you inputting?') break elif user_input == 'no': break else: print('Please try again') continue
哈哈,自己练习一下装饰器,又变着方式玩一下:
def foo(func): def wrapper(*args,**kwargs): while True: user_input = input('Welcome to you!Do you want play? yes/no:').strip() if user_input == 'yes': func(*args,**kwargs) break elif user_input == 'no': break else: print('Please try again') continue return wrapper @foo def count(): x = input('X:').strip() y = input('Y:').strip() if x.isdigit() and y.isdigit(): x = int(x) y = int(y) if x > y: print('The bigger number is:%s,so \033[42mX\033[0m is the biggest' % x) else: print('The bigger number is:%s,so \033[42mY\033[0m is the biggest' % y) else: print('Bitch,what the fuck are you inputting?') count()
例2:现将以上代码改成三元表达式:
代码如下:
while True: user_input = input('Welcome to you!Do you want play? yes/no:').strip() if user_input == 'yes': x = input('X:').strip() y = input('Y:').strip() if x.isdigit() and y.isdigit(): x = int(x) y = int(y) res = x if x > y else y print('The bigger number is:%s' % res) else: print('Bitch,what the fuck are you inputting?') break elif user_input == 'no': break else: print('Please try again') continue
例3:三元表达式的返回值
因为res是三元表达式的返回值,所以,三元表达式也可以不返回x和y的值,用来返回其它值:
代码如下:
while True: user_input = input('Welcome to you!Do you want play? yes/no:').strip() if user_input == 'yes': x = input('X:').strip() y = input('Y:').strip() if x.isdigit() and y.isdigit(): x = int(x) y = int(y) res = 'X is the biggest' if x > y else 'Y is the biggest' print(res) else: print('Bitch,what the fuck are you inputting?') break elif user_input == 'no': break else: print('Please try again') continue
例4:用三元表达式写一个比较4个值中的最大值的程序。
# 比较4个数中最大的数 num1 = int(input('num1:')) num2 = int(input('num2:')) num3 = int(input('num3:')) num4 = int(input('num4:')) # 比较num1和num2,得到结果 def max1_2(num1,num2): return num1 if num1 > num2 else num2 res1 = max1_2(num1,num2) # 比较num3和num4,得到结果 def max3_4(num3,num4): return num3 if num3 > num4 else num4 res2 = max3_4(num3,num4) # 用内置函数max去比较以上两个函数的结果,得到最终结果。 print(max(res1,res2))
2、列表解析:
(三元表达式的另一种形式)
列表的优点是取值方便,缺点就是太占用内存。
格式:
例:将字串中的每个字符以改成大写的形式放至一个列表中
s = 'xiaobai'
res = [i.upper() for i in s] # 条件成立的情况下将值往左边放,得到列表,循环后面还可以跟条件
print(res)输出结果:
['X', 'I', 'A', 'O', 'B', 'A', 'I'] # 最终得到一个列表的形式。
例:从用户输入的值中找出大于100的数。
之前是定义一个新的空列表,再用for循环加上判断条件得到想要的结果:代码如下:
num1 = int(input('Please input a num1:')) num2 = int(input('Please input a num2:')) num3 = int(input('Please input a num3:')) num4 = int(input('Please input a num4:')) num5 = int(input('Please input a num5:')) num6 = int(input('Please input a num6:')) num7 = int(input('Please input a num7:')) num8 = int(input('Please input a num8:')) num9 = int(input('Please input a num9:')) l = [num1,num2,num3,num4,num5,num6,num7,num8,num9,] l_new = [] for item in l: if item > 100: l_new.append(item) print(l_new)
现在只要用列表解析的方式就能用简短的代码得到结果。代码如下:
num1 = int(input('Please input a num1:')) num2 = int(input('Please input a num2:')) num3 = int(input('Please input a num3:')) num4 = int(input('Please input a num4:')) num5 = int(input('Please input a num5:')) num6 = int(input('Please input a num6:')) num7 = int(input('Please input a num7:')) num8 = int(input('Please input a num8:')) num9 = int(input('Please input a num9:')) l = [num1,num2,num3,num4,num5,num6,num7,num8,num9,] res = [i for i in l if i > 100] print(res)
3、生成器表达式
相比列表解析更省内存
格式:
与列表解析相似,只是把中括号换成小括号。列表解析得到的一定是一个列表,而生成器表达式得到的是个生成器。
例:
g = (i for i in range(10000000000000000000000000000)) # 如果用列表解析去产生一个列表,计算机一定会崩溃。
# 循环后面还可以跟条件
print(g) # 打印得到一个生成器
print(next(g)) # 取值
print(next(g)) # 取值
print(next(g)) # 取值
print(next(g)) # 取值
......