目录
迭代
迭代是重复反馈过程的活动,其目的通常是为了逼近所需的目标或结果,每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值,单纯的重复并不是迭代
count = 0
while count < 5:
count += 1
每次 count 都会基于上次的结果 再加 1,最终逼近所需要的结果
为什么要用迭代器
def func(items):
i = 0
while i < len(items):
print(items[i])
i += 1
非常明显的能够观察到,这并不能适用于所有的数据类型取值,它依赖于索引。
所以需要考虑用一种统一的方式 ( 可以不依赖于索引以及 key )
迭代器的使用
可迭代对象:内置有_ _iter _ _方法
调用:可迭代对象 . _ _iter _ _( )–> 返回迭代器对象
迭代器对象:内置有_ _iter _ _方法 和 _ _next _ _方法
调用:迭代器对象 . _ _next _ _( )–> 返回的是下一个值
调用:迭代器对象 . _ _iter _ _( )–> 返回的是就是迭代器本身,指向同个内存地址,方便 for 循环的底层设计方便
str = 'hello'
iter_str = str._ _iter_ _()
print(iter_str.__next__()) # h
print(iter_str.__next__()) # e
print(iter_str.__next__()) # l
print(iter_str.__next__()) # l
print(iter_str.__next__()) # 0
print(iter_str.__next__()) # 报错,已经取完了
双下划线的 方法,一般是不用来 直接调用的,而是某种条件下自动触发的
达到同样的调用效果,方便调用的一般都是类似 iter( )、next ( ),括号中放入参数
异常捕获
什么是异常
异常是程序发生错误的信号。程序一旦出现错误,便会产生一个异常,若程序中没有处理它,就会抛出该异常,程序的运行也随之终止。在Python中,错误触发的异常如下
而错误分成两种,一种是语法上的错误SyntaxError,这种错误应该在程序运行前就修改正确
另一类就是逻辑错误,是程序员在写程序是出现的常见错误
异常处理
为了保证程序的容错性与可靠性,即在遇到错误时有相应的处理机制不会任由程序崩溃掉,我们需要对异常进行处理,处理的基本形式为
try:
被检测的代码块
except 异常类型:
检测到异常,就执行这个位置的逻辑
例如:
try:
print('start...')
print(x) # 引用了一个不存在的名字,触发异常NameError
print('end...')
except NameError as e: # as语法将异常类型的值赋值给变量e,这样我们通过打印e便可以知道错误的原因
print('异常值为:%s' %e)
print('run other code...')
#执行结果为
start...
异常值为:name 'x' is not defined
run other code...
本来程序一旦出现异常就整体结束掉了,有了异常处理以后,在被检测的代码块出现异常时,被检测的代码块中异常发生位置之后的代码将不会执行,取而代之的是执行匹配异常的except子代码块,其余代码均正常运行。
当被检测的代码块中有可能触发不同类型的异常时,针对不同类型的异常:
如果我们想分别用不同的逻辑处理,需要用到多分支的except(类似于多分支的elif,从上到下依次匹配,匹配成功一次便不再匹配其他)
try:
被检测的代码块
except NameError:
触发NameError时对应的处理逻辑
except IndexError:
触发IndexError时对应的处理逻辑
except KeyError:
触发KeyError时对应的处理逻辑
try:
被检测的代码块
except (NameError,IndexError,TypeError):
触发NameError或IndexError或TypeError时对应的处理逻辑
如果我们想捕获所有异常并用一种逻辑处理,Python提供了一个万能异常类型Exception
try:
被检测的代码块
except NameError:
触发NameError时对应的处理逻辑
except IndexError:
触发IndexError时对应的处理逻辑
except Exception:
其他类型的异常统一用此处的逻辑处理
在多分支except之后还可以跟一个else(else必须跟在except之后,不能单独存在),只有在被检测的代码块没有触发任何异常的情况下才会执行else的子代码块
try:
被检测的代码块
except 异常类型1:
pass
except 异常类型2:
pass
......
else:
没有异常发生时执行的代码块
怎么使用异常处理
如果错误发生的条件是“可预知的”,我们应该用if来进行”预防”,如下
age=input('input your age>>: ').strip()
if age.isdigit(): # 可预知只有满足字符串age是数字的条件,int(age)才不会触发异常,
age=int(age)
else:
print('You must enter the number')
如果错误发生的条件“不可预知”,即异常一定会触发,那么我们才应该使用try…except语句来处理。例如我们编写一个下载网页内容的功能,网络发生延迟之类的异常是很正常的事,而我们根本无法预知在满足什么条件的情况下才会出现延迟,因而只能用异常处理机制了
import requests
from requests.exceptions import ConnectTimeout # 导入requests模块内自定义的异常
def get(url):
try:
response=requests.get(url,timeout=3)#超过3秒未下载成功则触发ConnectTimeout异常
res=response.text
except ConnectTimeout:
print('连接请求超时')
res=None
except Exception:
print('网络出现其他异常')
res=None
return res
get('https://www.python.org')
for 循环原理
使用while循环的实现方式如下
goods=['mac','lenovo','acer','dell','sony']
i=iter(goods) #每次都需要重新获取一个迭代器对象
while True:
try:
print(next(i))
except StopIteration: #捕捉异常终止循环
break
for 循环又称为迭代循环,in后可以跟任意可迭代对象,上述while循环可以简写为
goods=['mac','lenovo','acer','dell','sony']
for item in goods:
print(item)
for 循环在工作时,首先会调用可迭代对象goods内置的__iter__方法拿到一个迭代器对象
然后再调用该迭代器对象的__next__方法将取到的值赋给item,执行循环体完成一次循环
周而复始,直到捕捉StopIteration异常,结束迭代
拓展:
迭代器对象内置的_ _iter _ _方法,就是为了让 for 循环在开发时很方便。
不论迭代器对象还是可迭代对象,都直接用 iter( )方法,再用 next( )方法,就可以迭代取值
这样,就不用判断是迭代器对象还是可迭代对象。
迭代器对象与可迭代对象的区别
"list 是可迭代对象"
list =[1,2,3]
for i in list:
print(i,end='')
print('---'*5)
for i in list:
print(i)
"""
结果为:
1 2 3
---------------
1 2 3
"""
"iter_list 是迭代器对象"
list =[1,2,3]
iter_list = iter(list)
for i in iter_list:
print(i,end='')
print('---'*5)
for i in iter_list:
print(i)
"""
结果为:
1 2 3
---------------
"""
结论:对于同一个迭代器对象,其中的值取完之后,再重新取是取不了的,但是可迭代对象会每次重新生成迭代器对象
迭代器的优缺点
基于索引的迭代取值,所有迭代的状态都保存在了索引中,而基于迭代器实现迭代的方式不再需要索引,所有迭代的状态就保存在迭代器中
优点:(也是用途)
1、为序列和非序列类型提供了一种统一的迭代取值方式。
2、惰性计算:迭代器对象表示的是一个数据流,可以只在需要时才去调用next来计算出一个值,就迭代器本身来说,同一时刻在内存中只有一个值,因而可以存放无限大的数据流,而对于其他容器类型,如列表,需要把所有的元素都存放于内存中,受内存大小的限制,可以存放的值的个数是有限的。
缺点:
1、除非取尽,否则无法获取迭代器的长度
2、只能取下一个值,不能回到开始,更像是‘一次性的’,迭代器产生后的唯一目标就是重复执行next方法直到值取尽,否则就会停留在某个位置,等待下一次调用next;若是要再次迭代同个对象,你只能重新调用iter方法去创建一个新的迭代器对象,如果有两个或者多个循环使用同一个迭代器,必然只会有一个循环能取到值。
生成器与关键字 yield
def func():
print('hello1')
yield 1
print('hello1')
yield 2
print('hello3')
yield 3
func() # 直接调用,发现什么都没有
g = func ()
print(g)
# 打印结果:g为一个 generator ,一个生成器对象
若函数体包含yield关键字,再调用函数,并不会执行函数体代码,得到的返回值即生成器对象
生成器内置有__iter__和__next__方法,所以生成器本身就是一个自定义的迭代器
res = next(g)
print(res)
# hello1
# 1
res = next(g)
print(res)
# hello2
# 2
res = next(g)
print(res)
# hello3
# 3
yield 与 return 的区别
相同点:返回值层面都一样
不同点:return 只能返回一次值,函数就会立即结束,但是 yield 一次返回一次值,可以多次 yield
def func():
count = 0
while True:
yield count
count += 1
res = func()
print(next(res)) # 0
print(next(res)) # 1
print(next(res)) # 2
... # 可以取出无穷个值
实例:自定义 range()
def my_range(start,end=None,step = 1):
# 没有给 end 传值时
if not end:
end = start
start = 0
while start < end:
yield start
start += step
"调用方式 1:"
g= my_range(0,3)
print(next(g))
# start
# 0
print(next(g))
# 1
print(next(g))
# 2
print(next(g)) # 顾头不顾尾,此时值已经取完了
# 打印end
# 并且报错 StopIteration
"调用方式 2:"
for i in my_range(100):
print(i)
生成器表达式
生成器内部的代码只有在调用__next__迭代取值的时候才会执行
res = (i for i in 'jason')
print(res)
# <generator object <genexpr> at 0x1130cf468>
print(res.__next__())
# 普通的求和函数
def add(n, i):
return n + i
# 生成器对象 返回 0 1 2 3
def test():
for i in range(4):
yield i
# 将test函数变成生成器对象
g = test()
# 简单的for循环
for n in [1, 10]:
g = (add(n, i) for i in g)
"""
第一次for循环
g = (add(n, i) for i in g)
第二次for循环
g = (add(10, i) for i in (add(10, i) for i in g))
"""
res = list(g) # list底层就是for循环 相当于对g做了迭代取值操作
print(res)
#A. res=[10,11,12,13]
#B. res=[11,12,13,14]
#C. res=[20,21,22,23]
#D. res=[21,22,23,24]
"""正确答案是C 诀窍就是抓n是多少即可"""
三元表达式
三元表达式是为了简化代码,一般二选一的时候推荐使用三元表达式
value1 if condition else value2
当条件成立时–>value1,不成立–>value2
# 如果是jason输出管理员,否则输出其他
# 方式一
name = input('请输入').strip()
if name == 'jason':
print('管理员')
else:
print('其他')
# 三元表达式
name = input('请输入').strip()
res = '管理员' if name == 'jason' else '其他'
print(res)