异常处理、迭代器、推导式、生成器

异常处理:
 导致程序运行过程中出现异常中断和退出的错误,我们称之为异常。
 为了保证程序的正常运行,提高程序的健壮性和可用性。我们应当尽量考虑全面。将可能出现的异常进行处理
 python处理异常的机制:
 基本语法:

# try:
# pass # 程序代码
# except Exception:
# pass # 程序异常使执行的代码
# try:
# print('123')
# a = 1/0
# except Exception as e:
# print(e)
# print('after except!')

# 运行结果:
# 123
# division by zero
# after except!

# 当程序发生的异常不在捕获列表中时,依然会抛出异常
# try:
# a = 1/0
# except IndexError as e: # IndexError 表示下标索引错误
# print(e)
# 运行结果报错:ZeroDivisionError: division by zero

try......except

 在python的异常中,有一个通用异常:Exception,它可以捕获任意异常

 1)很多时候程序只会弹出那么几个异常,没有必要针对所有异常进行捕获,那样的效率会很低

 2)根据不同的异常种类,制定不同的处理措施,用于准确判断错误的类型,存储错误日志

(1)函数嵌套处理异常

# def f1():
#     a = 1/0
# def f2():
#     print('f2')
#     f1()
# def f3():
#     print('f3')
#     f2()
# try:
#     f3()
# except Exception as e:
#     print(e)

# 运行结果:
# f3
# f2
# division by zero

(2)try.....except的嵌套

# 如果一个异常没有与任何的except匹配,那么这个异常将会传递给上层的try中,直到程序最顶端如果还没有被捕获,那么将弹出异常

# try:
#     try:
#         a = 1/0
#     except IndexError as e1:
#         print('this is e1:',e1)
# except ZeroDivisionError as e2:
#     print('this is e2:',e2)

# 运行结果:this is e2 division by zero

(3)使用一个try跟多个except(推荐使用)

1 # try:
2 #     a = 1/0
3 # except IndexError as e1:
4 #     print('this is e1:',e1)
5 # except ZeroDivisionError as e2:
6 #     print('this is e2:',e2)

7 # 运行结果:this is e2: division by zero

# (4)或者except后面跟一个元组,元组中包含多个异常类,有点像Exception的特殊形式

1 # try:
2 #     a = 1/0
3 # except (IndexError,ZeroDivisionError):
4 #     print('error')

try...except...finally(else)...

# finally和else子句:

# else子句必须放在所有的except子句之后,会在try子句没有发生异常的时候执行

# finally子句无论try和except情况如何finally子都会执行

1 # try:
2 #     a = 1/0
3 # except ZeroDivisionError as e:
4 #     print('error')
5 # else:
6 #     print('else')
7 # finally:
8 #     print('finally')

raise:主动抛出异常

# 主动抛出异常:raise

# python内置了一个关键字raise,可以主动触发异常,作为警告或者特殊处理

# 使用raise抛出自定义的异常

# 例题:用户录入自己的性别:1表示男 2表示女

# class MyError(Exception):
#     def __init__(self,msg):
#         self.msg = msg
#     def __str__(self):
#         return self.msg

# sex = input('enter your sex:')
# if sex!='1' and sex!='2':
#     raise MyError('您输入的性别有误!')
# else:
#     print('right!')

# 运行结果:报错终止程序
# raise MyError('您输入的性别有误!')
# __main__.MyError: 您输入的性别有误!

自定义异常

# 自定义异常应该继承Exception类,直接继承或间接继承都可以

# class MyErr(Exception):
#     def __init__(self,msg):
#         self.msg = msg
#     def __str__(self):
#         return self.msg

# err = MyErr('find error range')
# print(err)  # find error range

迭代器

# 迭代:通过for循环遍历"对象"的每一个过程,这里的对象指的就是可迭代对象。for循环遍历的只能是可迭代的对象

# 可迭代对象:list/tuple/string/dict/set/bytes/文件都是可迭代的数据类型/可迭代对象

# 如何判断一个对象是否为可迭代对象:可以通过collections模块作用在isinstance中来判断对象是否可迭代

# from collections import Iterable  # Iterable迭代类型
# from collections.abc import Iterable
# class Test():
#     pass
# alist = [1,2,3]
# str = 'abc'
# t = Test()
# print(isinstance(alist,Iterable))  # True

# print(isinstance(str,Iterable))     # True

# print(isinstance(t,Iterable))       # False

python提供了不依赖于索引取值的方式就是迭代器迭代取值

# 迭代器是一种可以被遍历的对象,并且能作用于next()函数

# 性质:迭代器对象从集合的第一个元素开始访问,直到所有元素被访问完结束。

# 迭代器只能往后遍历不能回溯,不像列表可以根据搜索引取值

迭代器作用:节省内存

iter()和next()

# 迭代器通常要实现两个基本方法:iter()/.__iter__()和next()/.__next__()

# 注:1、可迭代对象并不一定是迭代器

#     2、常见的数据结构,字符串、列表、元组都属于可迭代对象,并不是迭代器

 

# 可以使用type()查看列表和迭代器类型的不同:

# 使用python内置的iter()方法创建迭代器对象

# from collections.abc import Iterable

# alist = [1,2,3]

# print(type(alist))   # <class 'list'>

 

调用可迭代对象的下的__iter__方法会将其装换成迭代器,当进行迭代时才会调用iter方法

# # 将列表转换成迭代器

# iter_alist = iter(alist)

# print(type(iter_alist))   # <class 'list_iterator'>

# 使用next()方法获取迭代器的下一个元素

from  collections.abc import Iterable
alist = [1,2,3]
iter_alist = iter(alist)
# print(next(iter_alist))
# print(next(iter_alist))
# print(next(iter_alist))

# 运行结果:
# 1
# 2
# 3

 使用for循环遍历迭代器:

# for i in iter_alist:
#     print(i)

d = {"a":1,"b":2,"c":3}
for k in d:
    print(k)

for循环的工作原理:(也可称之为迭代器循环)

1、d.__iter__()得到一个迭代器对象

2、迭代器对象.__next__()拿到一个返回值,然后该返回值赋给k

3、循环往复步骤2,直到抛出StopInteration异常,for循环会捕捉异常结束循环

 

总结:

# 可迭代对象的优缺点:

# 优点:可以直观查看里面的对象,如直接查看列表的内容

# 缺点:全部内容要加载至内存中,故占用内存

# 迭代器的优缺点:

# 优点:提供了一种通用不依赖索引的迭代取值方式;

# 节省内存,迭代器在内存中相当于只占一个数据的空间:因为每次取值上一条数据会在内存释放,加载当前的此条数据。

# 缺点:因为有next()方法,即只能往后取值,不能往前,取值不能取指定的某一个值;无法预测迭代器的长度

 

# 总结:迭代器和可迭代的区别

可迭代对象 Iterable  迭代器Iterator

# 1、凡是可作用于for循环的对象都是可迭代类型

# 2、凡是可作用于next()函数的对象都是迭代器对象

# 3、list、dict、str等是可迭代对象但不是迭代器,因为next()函数无法调用他们

# 可以通过iter()函数将它们转换成迭代器

 自定义迭代器

# 为了让我们自己写的类成为一个迭代器,需要在类里实现__iter__()和__next__()方法

# 实际上在使用next()函数的时候,调用的就是迭代器对象__next__方法

# python要求迭代器本身也是可迭代的,

# 所以我们还要为迭代器实现__iter__方法,而__iter__方法要返回一个迭代器

# 迭代器自身正是一个迭代器,所以迭代器的__iter__方法返回自身即可

# 例题:#迭代生成指定范围数列的平方值

# class IterObj():
#     def __init__(self,start,end):
#         self.start = start
#         self.end = end
#         self.index = 1

#     def __iter__(self):
#         print('iter')
#         return self

#     def __next__(self):
#         if self.index > self.end:
#             raise StopIteration
#         res = self.index **2
#         self.index +=1
#         print('next')
#         return res

# i = IterObj(1,5)
# print(type(i))
# for j in i:
#     print(j)

# 斐波那切数列实现

# class IterObj():
#     def __init__(self,count):
#         self.count = count
#         self.index = 0 # 记录生成第几个元素
#
#         self.num1 = 0
#         self.num2 = 1
#         self.num3 = 1
#     def __iter__(self):
#         return self

#     def __next__(self):
#         if self.index > self.count:
#             raise StopIteration
#         value = self.num1
#         self.index += 1
#         self.num1,self.num2,self.num3 = self.num2,self.num3,self.num2+self.num3
#         return value

# obj = IterObj(10)
# for i in obj:
#     print(i)

# 实现类似iter功能的类:可以使得将列表转换成迭代器

# class IterObj():
#     def __init__(self,alist):
#         self.alist = alist
#         self.index = 0

#     def __iter__(self):
#         return self

#     def __next__(self):
#         if self.index >= len(alist):
#             raise StopIteration
#         value = alist[self.index]
#         self.index += 1
#         return value

# obj = IterObj([1,2,3])
# for i in obj:
#     print(i)

推导式

 (1)列表推导式:

语法:[表达式 for item in 可迭代对象 if 条件] 返回结果为列表

lst = [x*2 for x in range(1,10)]

# 增加条件语句:alist = [x*x for x in range(1,11) if x%2==0]  

# 多重循环:

# 同时循环a和b两个变量:re = [a+b for a in '123' for b in 'abc']

 (2)字典推到式

语法:{键:值 for k in 可迭代对象 if 条件}

字典推导式 {k:v for k,v in dic.items()}

# dic = {x:x**2 for x in (2,4,6)}  # x:x**2指字典的key:value

例1 将一个字典的键值对倒换

dic = {"id":1001,"name":"yuan","age":22,"weight":"70kg"}

new_dic={v:k for k,v in dic.items()}

# {1001: 'id', 'yuan': 'name', 22: 'age', '70kg': 'weight'}

例2:将一个字典的键转换成大写

new_dic={k.upper():v for}

new_dic={k.upper():v for k,v in dic.items()}

print(new_dic)

# {'ID': 1001, 'NAME': 'yuan', 'AGE': 22, 'WEIGHT': '70kg'}

例3:保留name与age的键值对

new_dic={k:v for k,v in dic.items() if k=='name' or k=='age'}

print(new_dic) # {'name': 'yuan', 'age': 22}

 (3)集合推导式

# a = {x for x in 'sfgfgh'}

 (4)元组推导式

# tup = (x for x in range(9))  # 是一个生成器

# print(tup)  # <generator object <genexpr> at 0x000001F55F9955F0>

# tup = tuple(x for x in range(9))

# print(tup)  # (0, 1, 2, 3, 4, 5, 6, 7, 8)

生成器

数据序列或集合内的元素的个数非常巨大,如果全造出来并放入内存,对计算机的压力非常大 如果元素可以按照某种算法推算出来,需要该元素的话那就计算到哪个元素,那么就可以在循环的过程中不断推算出后续的元素,而不必创建完整的元素集合,从而节省大量的空间。在Python中,这种一边循环一边计算出元素的机制,称为生成器:generator。 **生成器是一种特殊的迭代器,生成器自动实现了”迭代器协议“(即iter方法和next方法)**,生成器就是自定义的迭代器

创建生成器 创建生成器的两种方式:

1)生成器推导式:

 

tup = (x for x in range(3))
# 可以通过next()函数获得generator的下一个返回值
next(tup)
print(type(tup)) # <class 'generator'>

# 通常情况下,使用for循环遍历生成器
for i in tup:
    print(i)

 

2)yield关键字创建生成器

在 Python中,使用yield返回的函数会变成一个生成器(generator)。 在调用生成器的过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield的值。并在下一次执行next()方法时从当前位置继续运行。下面重点理解yield关键字的使用:

*yield 是一个类似 return 的关键字,只是这个函数返回的是个生成器

*当你调用这个函数的时候,函数内部的代码并不立马执行 ,这个函数只是返回一个生成器对象

*当你使用for进行遍历的时候或者调用next函数后,函数中的代码才会执行

def mySum():
    print('before')
    print('after')
    yield 'result'

f = mySum()
print(type(f),next(f))
# 运行结果:
# before
# after
# <class 'generator'> result

思考:下述函数的执行结果是什么?

def yieldTest():
    i = 0
    while i < 3:
      temp = yield i 
      print(temp)
      i += 1

obj = yieldTest() # 返回的生成器对象
print(next(obj))
print(next(obj))
运行结果:
0   # yield i 停止
None  # 将None赋值给temp
1    # i+1 = 1 yield i 停止

next()会触发函数体代码的运行,然后遇到yield停下来,将yield后的值当作本次调用的结果返回

for i in yieldTest():  # 就是迭代器
    print(i)
运行结果:
0
None
1
None
2
None

总结yield:

有了yield关键字,我们就有了一种自定义迭代器的实现方式,yield可以用于返回值,但不同于return,函数一遇到return就结束了,而yield可以保存函数的运行状态挂起函数用来返回多次值

 

posted @ 2021-09-10 16:02  多啦a梦与时光机  阅读(39)  评论(0编辑  收藏  举报