013、Python--异常捕获、迭代器、生成器、三元表达式

迭代

迭代是重复反馈过程的活动,其目的通常是为了逼近所需的目标或结果,每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值,单纯的重复并不是迭代

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)

版权声明:本文为weixin_43988680原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。