首页 > 技术文章 > 迭代器,for循环本质,迭代取值与索引取值对比,迭代器优缺点,异常处理

gengfenglog 2021-01-02 13:07 原文

迭代器

u=1466229775,3995975382&fm=26&fmt=auto

  • 什么是迭代器

    • 迭代器指的是迭代取值的工具
  • 什么是迭代

    • 迭代即更新换代,每次的更新都必须依赖于上一次的结果
l = [111, 222, 333]  # 遍历列表,索引,迭代取值
nums = "hello" 

def get(l):  # 函数就是取值工具
    i = 0
    while i < len(l):
    	print(l[i])  # 每次的结果都在上一次的基础之上
    	i += 1
get(l)   
  • 为何要用迭代器

    • 1、迭代器提供了一种不依赖于索引迭代取值的方式
    • 2、节省内存

WechatIMG582_gaitubao_296x305


可迭代的对象

  • 内置有__iter__方法的对象都叫可迭代的对象

    • 调用可迭代对象的__iter__方法,返回的是迭代器对象

# 内置的类型都是可迭代的对象
s = "abc"  # 字符串
l = [11, 22, 33]  # 列表
d = {'k1': 111}  # 字典
t = (11, 22, 33)  # 元组
se = {11, 22, 33}  # 集合
f = open('a.txt',mode='wt')  # 文件对象本身就是迭代器对象

"""
含有__iter__的有
    字符串 列表 字典 元组 集合 文件对象
上述通常为可迭代对象
内置的意思是可以通过点的方式直接查看到的
"""
print(d)  # {'k1': 111}
print(d.__iter__())  # <dict_keyiterator object at 0x7fbb4f787db0>
print(iter(d))  # <dict_keyiterator object at 0x7fbb4f787db0>
print(d.__len__())  # 1
print(len(d))  # 1
"""
可迭代对象调用__iter__方法会变成迭代器对象(老母猪)

__iter__方法在调用的时候还有一个简便的写法iter()
一般情况下所有的双下方法都会有一个与之对应的简化版本 方法名()
"""

迭代器对象

  • 内置既有__iter__方法又有__next__方法的对象都叫迭代器对象

    • 调用迭代器对象的 __iter__方法得到还是迭代器对象本身,就跟没调用一样

    • 调用迭代器对象的__next__方法返回下一个值,不依赖索引

    • 可以一直调用__next__直到取干净,则抛出异常StopIteration(停止迭代)

# 迭代器直接取值方式
l = [111, 222, 333]

l1 = l.__iter__()  # 将可迭代对象转为迭代器对象,源列表没动,产生新的

print(l1)  # <list_iterator object at 0x7fe1708378e0>

print(l1.__next__())  # 111 迭代器对象执行__next__方法其实就是在迭代取值(for循环)
print(l1.__next__())  # 222
print(l1.__next__())  # 333
print(l1.__next__())  # 取不到了抛出异常 StopIteration

'''
__next__方法在调用的时候也有一个简便的写法iter()
'''
res = iter(l)
print(next(res))  # 111
print(next(res))  # 222
print(next(res))  # 333


# 循环打印出列表中每个元素
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55]
l_iter = iter(l)  # 把可迭代对象先转成迭代器对象

while True:  # 循环执行__next__取值
    try:  # 异常处理:检测代码是否会报错
        res = next(l_iter)
        print(res)
    except StopIteration:  # 捕捉异常,结束循环
        break

# 用for循环两行代码替代上面方法  
for res in l:  
    print(res)
    
"""
for循环内部原理
	1.将关键字in后面的数据先调用__iter__方法转为迭代器对象
	2.循环执行__next__方法
	3.取完之后__next__会报错,但是for循环会自动捕获该错误并处理
"""

迭代器的优缺点

优点:

  	1、为序列和非序列类型提供了一种统一的迭代取值方式。
  	
        2、惰性计算:迭代器对象表示的是一个数据流,可以只在需要时才去调用__next__来计算出一个值,就迭代器本身来说,
        同一时刻在内存中只有一个值,因而可以存放无限大的数据流,而对于其他容器类型,如列表需要把所有的元素都存放于
        内存中,受内存大小的限制,可以存放的值的个数是有限的。

缺点:

  	1、除非取尽,否则无法获取迭代器的长度
  	
  	2、只能取下一个值,不能回到开始,更像是‘一次性的’,迭代器产生后的唯一目标就是重复执行next方法直到值取尽,否则
        就会停留在某个位置,等待下一次调用next;若是要再次迭代同个对象,你只能重新调用__iter__方法去创建一个新的迭代器
        对象,如果有两个或者多个循环使用同一个迭代器,必然只会有一个循环能取到值。



异常处理

yichan1


  • 什么是异常

    • 异常是程序错误发生的信号。程序一旦出现错误,就会抛出异常, 异常发生后如果没有解决方案则会导致整个程序结束


异常三个重要组成部分

  • 1. Traceback

    • 翻到最下面从下往上的第一个蓝色字体鼠标左键点击即可跳转到错误的代码所在的行
  • 2. XXXError

    • 错误的类型
  • 3. 错误类型冒号后面的内容

    • 错误的详细原因(很重要,仔细看完之后可能就会找到解决的方法)

错误分为两种

  • 1、语法错误

    • 不被允许的,这种错误在程序运行前就必须修改正确
  • 2、逻辑错误

    • 针对可以控制的逻辑错误,应该直接在代码层面解决

    • 针对不可以控制的逻辑错误,应该采用try...except...(一种异常产生之后的补救措施)


可以控制的逻辑错误

  • 如果错误发生的条件是可预知的,我们需要用if进行处理:在错误发生之前进行预防
age = input(">>>: ").strip()
if age.isdigit():
    age = int(age)

    if age > 19:
        print('too big')
    elif age < 19:
        print('too small')
    else:
        print('you got it')
else:
    print('必须输入数字')
    
'''修改逻辑错误的过程其实就是在从头到尾理清思路的过程'''

不可以控制的逻辑错误

  • 如果错误发生的条件是不可预知的,则需要用到try...except:在错误发生之后进行处理
"""
如何用try...except
基本语法结构:
	try:
	    有可能会出现异常的代码
	except 异常的类型 as e:
   	    异常之后对应的处理机制(e是异常的详细信息)
	except 异常的类型 as e:
            异常之后对应的处理机制(e是异常的详细信息)
        except (异常的类型1,异常的类型2) as e:
            异常之后对应的处理机制(e是异常的详细信息)
        except Exception:  # 万能异常,当上面所有的异常处理都对不上最终运行此异常处理
            异常之后对应的处理机制(e是异常的详细信息)
        else:
            没有发生异常时要执行的代码
        finally:
            无论异常与否,都会执行该代码,通常用来进行回收资源的操作
"""

# 例1
try:
    print(111)
    print(222)
    l=[11, 22, 33]
    l[100]  # 异常
    print(3333)
except IndexError as e: # 捕捉异常匹配成功并打印e,程序不会崩
    print(e) 
    
'''
111
222
list index out of range 列表索引超出范围
'''

# 例2
try:
    print(111)
    l=[11, 22, 33]
    l[100]
except KeyError as e:  # 异常类型对不上匹配不成功,程序就崩了
    print(e)
'''
111
222
Traceback (most recent call last):
  File "/Users/gengfeng/Python20/Day14/异常处理.py", line 16, in <module>
    l[100]
IndexError: list index out of range
'''

# 例3
try:
    print(111)
    print(222)
    l=[11,22,33]
    l[0]
    dic = {'k1':111}
    print(3333)
except Exception as e:  # 万能异常,什么异常都能对上
    print(e)
else:  # try内代码块没有异常则执行
    print('else的代码')
finally:  # 无论异常与否,都会执行该模块,通常是进行清理工作
    print('finally的代码')
    
'''
111
222
3333
else的代码
finally的代码
'''

"""
异常捕获句式和万能异常
    1.有可能会出现错误的代码才需要被监测
    2.被监测的代码一定要越少越好
    3.异常捕获使用频率越低越好
"""

比较常见的异常

l=[]
l[100]

'''
IndexError: list index out of range
索引错误:列表索引超出范围 
'''

dic={}
dic["k1"]
'''
KeyError: 'k1'  
key错误:'k1'
'''
 
int("asdb")
'''
ValueError: invalid literal for int() with base 10: 'asdb'
值错误:无效的int()的基数为10:'asdb'  
'''

1/0  
'''
ZeroDivisionError: division by zero 
# 零除法错误:被零除    
''' 

def func(x):
    pass

func() 
'''
TypeError: func() missing 1 required positional argument: 'x'
类型错误:func()缺少1个必需的位置参数:'x'
'''

x
'''
NameError: name 'x' is not defined   
名称错误:名称'x'未定义
'''
    
print('asdfadf'
'''     
SyntaxError: unexpected EOF while parsing  
语法错误:解析时出现意外的EOF     
'''          

断言(了解)

  • 判断条件达到标准,如果不达到标准就抛出异常
  • 测试程序的时候可以用,测试完后应该删掉
l = [111, 222]
# 判断如果l的长度不等于3就主动抛出异常
if len(l) != 3:      
    raise Exception('必须达到三个值')  # Exception: 必须达到三个值 
'''  
assert:判断条件达到标准正常执行程序,如果不达到标准就抛出异常 
raise:主动抛出异常,为别人定规则的时候就可以用到主动抛异常
'''  
# 断定l的长度等于3,断定失败就抛出异常      
assert len(l) == 3  # 效果跟上面一样(建议使用)
print('后续代码。。。')

# 主动抛出索引错误
raise IndexError("索引错误")  # IndexError: 索引错误

ta


异常的种类

  • 在python中不同的异常可以用不同的类型去标识,一个异常标识一种错误
# 常见异常

IOError 输入/输出异常;基本上是无法打开文件

ImportError 无法引入模块或包;基本上是路径问题或名称错误

IndentationError 语法错误(的子类) ;代码没有正确对齐

IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]

KeyError 试图访问字典里不存在的键

KeyboardInterrupt Ctrl+C被按下

NameError 使用一个还未被赋予对象的变量

SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
                              
TypeError 传入对象类型与要求的不符合
                              
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
                  导致你以为正在访问它
                              
ValueError 传入一个调用者不期望的值,即使值的类型是正确的
                              
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x                              

推荐阅读