Python 迭代器 与 异常处理

迭代介绍

什么叫做迭代,迭代可以理解为更新换代,每一次迭代的过程都需要依赖于上一次的结果。迭代器有两个基本的方法:iter()next()


		while True:
		    print(666)

像这种单纯的循环并不能称之为迭代,因为它没有和上一次的结果有关系。

		list_a = ['XWeXiang', 123]
		i = 0
		while i < len(list_a):
		    print(666)
		    i += 1

这种属于迭代,但是并没有取值。

		list_a = ['XWeXiang', 123]
		i = 0
		while i < len(list_a):
		    print(list_a[i])
		    i += 1

此时可以迭代列表中的元素并取出。

可迭代对象

什么是可迭代对象?如果某一个对象中有着内置方法 __iter__ 的称之为可迭代对象
这个方法读作双下iter方法。

如何判断一个对象是否为可迭代的对象

方式一
通过 dir() 函数来判断该对象是否有 __iter__ 方法
	
		print(dir(list_a))

方式二
直接在该对象加点就能弹出它能用的方法,相对于方式一更简单。

哪些是可迭代的对象

通过上面的方法我们可以得到可迭代的对象类型有:字符串、列表、字典、元组、集合、文件
我们可以发现这些都支持 for 循环,其实是因为它们是可迭代对象才支持 for 循环。

迭代器对象

迭代器

迭代器是一个可以记住遍历的位置的对象,它会将遍历出的元素先储存起来,当需要用的时候才调用,极大的
减少占用内存的空间。

迭代器对象

如何判断是否为迭代器对象,迭代器对象的特征是它有列个方法 __iter__方法和__next__方法,有这俩方
法才是迭代器对象。
'需要注意的是,文件对象自带__iter__方法和__next__方法,既是可迭代对象优势迭代器对象。'
可迭代对象如何能编变成迭代器对象,可迭代对象在调用了方法 __iter__ 之后就成为了一个迭代器对象

代码示例
		list_a = ['XWeXiang', 123]
		print(list_a.__iter__())
输出结果
		<list_iterator object at 0x000001D5CBB6A8F0>

此时返回的值是迭代器对象。

''' 
需要注意的是,如果已经调用了方法  __iter__ 成为迭代器对象之后再一次调用方法 __iter__ 返回的是
它本身而不是重新创建一个迭代器对象。

也就是说 list_a.__iter__() 等价于 list_a.__iter__().__iter__().__iter__()
'''

迭代器对象取值

知道了如何成为迭代器对象后,就需要了解怎么从迭代器对象中取值
使用的方法是 __next__方法,此方法会返回下一个迭代器对象
代码示例一
		list_a = ['XWeXiang', 123]
		
		x = list_a.__iter__()
		print(x.__next__())
		print(x.__iter__().__next__())

输出结果
		XWeXiang
		123
		
'此时如果在调用一次 __next__方法的话程序就会报错,因为里面的元素都已经取完了。'		
代码示例二
		list_a = ['XWeXiang', 123]
		
		print(list_a.__iter__().__next__())
		print(list_a.__iter__().__next__())
		print(list_a.__iter__().__iter__().__next__())
		
如果是这种情况下的话,打印的结果都一样,都为第一个元素。因为在打印的时候每一次都生成了新的迭代器
对象。

而在示例一中,因为已经提前生成了迭代器,所以在打印的时候不论调用多少次 __iter__()返回的值都是
自身,也就是不变。

方法的简写

有很多双下方法其实都有简便写法(不是全部都有)

__方法名__  等价于  方法名()
例如  __iter__() 方法和__next__() 方法
这俩种方法可以简写成 iter() next()
例如 __len__() 可以简写成 len()

for循环内部原理

我们在用 for 循环的时候也会逐个取出可迭代对象的值,其实它的底层原理很像下面的代码示例

代码示例

		list_a = ['XWeXiang', 123]
		i = 0
		x = list_a.__iter__()
		while i < len(list_a):
		    print(x.__next__())
		    i += 1

输出结果
		XWeXiang
		123

for 循环的原理也是先调用__iter__()将可迭代对象变成迭代器对象,'''(文件自带 __iter__() 但是
在循环中在调用一遍结果也不变''' 然后用 __next__ 方法来取值,只不过在 for 中不需要得到可迭代对
象的长度,我们需要长度是因为取完值继续取会报错,但是 for 循环对报错进行了处理,异常处理会在下面
讲到。

异常处理

异常,也就是程序出错,会导致程序停止运行,俗称 bug 。
我们先看下异常长啥样,混个脸熟,毕竟以后天天见…
在这里插入图片描述

line 1 表示的是在第一行出错,我们可以点击蓝色的地方跳转到该位置。
SyntaxError 表示的是错误的类型,类型有很多
错误类型后面的是具体错误的原因。

异常的分类

异常有俩种分类,为 语法异常 和 逻辑异常。

1. 语法异常
语法异常或者叫解析错误,比如说丢了个冒号,丢了个括号之类的,这种是不被允许的,被发现只能删库跑路
了(开玩笑)

2. 逻辑异常
例如代码动态获取到了一个字符串类型的数据但是调用了列表的内置方法等运行期检测到的错误

异常的类型

TypeError     		类型错误  
AttributeError    	属性错误
NameError   		变量名错误
SyntaxError   		语法错误
KeyError  			key错误
IndexError  		索引错误
IndentationError  	缩进错误

...还有很多种,没有你想不到的错误哈哈

异常的处理

针对可能会出错的代码 也可以自己提前写好处理措施

try/except 语句
处理异常的语法

		try:
			可能会出错的代码
		except 错误的类型一 as e:
			代码A
		except 错误的类型二 as e:  # 这里的 e 是详细的错误提示
			代码B

上面的 try/except 语句有点像 if 语句,except 可以有多个,如果错误的类型是类型一,执行代码 A,
如果错误的类型是类型二,执行代码 B。如果没有错误则执行完 try 的语句就结束。
代码示例

		try:
		    if len('XWenXiang') > 6:
		        name
		    else:
		        pass
		except NameError:  # 这里的 e 是详细的错误提示
		    print('错误在这里')
			print(e)

输出结果

		错误在这里
		name 'name' is not defined


当代码发生错误且类型为 NameError 的时候,打印字符串。e 是详细的错误提示

万能错误类型

当我们不知道会报什么错的时候,可以用通用的错误类型 Exception

代码示例
		try:
		    if len('XWenXiang') > 6:
		        name
		    else:
		        pass
		except Exception as e:
		    print('错误在这里')
		    print(e)

只是修改了 except 的错误类型,如果不是 Exception 且错误的类型不符合的话,处理异常的代码不会执
行。而 Exception 可以对任意类型异常使用。
try/except…else 语句

和 try/except 语句一样,只不过多了一个 else 分支。

代码示例
		try:
		    if len('XWenXiang') > 6:
		        name = 66
		    else:
		        pass
		except Exception as e:
		    print('错误在这里')
		    print(e)
		else:
		    print('没有错')

输出结果
		没有错
		
else 语句是在被检测的代码'没有错误'的时候执行的
try/except…finally 语句
代码示例
		try:
		    if len('XWenXiang') > 6:
		        name 
		    else:
		        pass
		except Exception as e:
		    print('错误在这里')
		    print(e)
		finally:
		    print('真厉害')

输出结果
		错误在这里
		name 'name' is not defined
		真厉害

finally 语句不管被检测的代码是否有错都执行
try/except…/else/finally 语句

它们可以一起使用

代码示例		
		try:
		    if len('XWenXiang') > 6:
		        name = 66
		    else:
		        pass
		except Exception as e:
		    print('错误在这里')
		    print(e)
		else:
		    print('没有错')
		finally:
		    print('真厉害')

输出结果
		没有错
		真厉害

当前被检测的代码并没有错误,所以会执行 else 的代码和不管错没错都执行的代码 finally

断言

断言使用的关键字是 assert ,用于判断一个表达式,在表达式条件为 false 的时候触发异常

断言语法
		assert expression [, arguments]

expression 是判断语句 
arguments 是报错的时候显示的语句
assert True
assert False,'66'

False 的时候会报错,报错显示 66
assert 后面跟着条件语句,返回 TrueFalse

所以 assert expression 相当于 

			if not expression:
				raise AssertionError  #(主动报错)
				

主动报错

主动报错就是使用 raise 语句抛出一个指定的异常

语法格式如下:
		raise [Exception [, args [, traceback]]]

代码示例
		raise NameError(996)
		
此时会报一个类型为 NameError 的错。

我们也可以使用万能的错位类型 'Exception' ,后面加括号,括号里跟着错误时显示的内容。

for 循环的本质

迭代所有的内容,并处理继续迭代报的错,类似于下面的代码

		x = iter(range(1, 10))
		while True:
		    try:
		        print(x.__iter__().__next__())
		    except StopIteration as e:
		        break

不过 for 循环是已经封装好的,我们日常也是使用 for 循环,很少使用底层的迭代。

迭代取值与索引取值的对比

1.索引取值
	优势:可以反复获取相同的元素 并且没有固定的方向
  	劣势:只能支持有序的容器类型 无序的无法取值兼容性没有迭代取值高
 
2.迭代取值
	优势:兼容所有的容器类型
 	劣势:取值的顺序永远都是从左往右 并且无法重复获取 去完就完了