鸡汤:
要时刻不断地给自己灌输一种思想:都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)) # 取值
......