匿名函数、常见的内置函数、可迭代对象、迭代器对象、for循环的内部原理、异常捕获

发布时间 2023-09-13 19:21:26作者: jntmwl

匿名函数

注意: 匿名函数一般不单独使用

匿名函数其实就是没有名字的函数 关键字 lambda
  #匿名函数也是必须加括号才能使用

匿名函数的语法格式:
    lambda 形参:返回值
    def index(形参):
        返回值
''''''''''''
res = lambda x,y:x+y
print(res(1, 2))

!

常见内置函数

1.map(映射)

map函数的功能就是映射

l1 = [1, 2, 3, 4, 5]
# 假设我们要所有的l1中的数据值+1
a = map(lambda i:i+1,l1)
print(list(a))

如果我们直接print(a),我们得到结果时map函数的相关代码,但是当我们用list方法把a转换成列表之后打印就可以看到转换后的结果

2.zip(拉链)

说白了就是压缩几组数据值。说细了就是将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的对象,一般会用list或dict方法输出为对应的类型,当对象为多个的时候,会根据数据值最少的那个参数为输出的个数,多余的部分就不会输出了。

l1 = [1, 2, 3,4,5,6]
l2 = ['a', 'b', 'c', 'd']
l3 = ['kevin', 'jerry', 'tank', 'oscar']
l4 = ['kevin1', 'jerry2', 'tank3', 'oscar4']

# [(1, 'a'), (2, 'b'), (3, 'c')]
# new_list = []
# for i in range(len(l1)):
#     new_list.append((l1[i], l2[i]))
#
# print(new_list) # [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'), (6, 'f')]

res = zip(l1, l2, l3, l4) # [(1, 'a', 'kevin'), (2, 'b', 'jerry'), (3, 'c', 'tank')]
print(list(res)) # [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]

3.max,min (最大值与最小值)

max和min默认情况下返回的是字典的key,也是按照key值比较的

d = {
    'kevin':1000,
    'jerry':30000,
    'Tank': 200000,
    'Oscar':100
}
'''注意 key是根据ASCll表进行比对的'''

# print(max(d)) # 结果为kevin
# print(min(d)) # 结果为Oscar

def index(x):
    return d[x] # 返回什么就安装什么进行比较

'''匿名函数搭配写法'''
res=max(d, key=lambda x:d[x])
res1=min(d, key=lambda x:d[x])
print(res,res1)

4.filter(筛选)

filter函数是筛选的意思:此函数会将可迭代对象中的每一位元素作为参数传递到func中,如果结果为True,则将此元素添加到结果列表中,filter()返回的结果是一个的新列表。

表现形式:filter(func,iterable)

其中func为函数名,可为lambda匿名函数,iterable为可迭代对象。

res = [11, 22, 33, 44, 55, 66]
# new_res = []
# for i in res:
#     if i > 30:
#         new_res.append(i)
# print(new_res)


def index(x):
    return x>30
# res1=filter(index, res) # 内部也是for循环
res1=filter(lambda x:x>30, res) # 内部也是for循环
print(list(res1))

可迭代对象

迭代:更新换代,每一次的更新都是基于上一次的结果

之前我们对于for循环为什么可以遍历没有做具体解释,这里就开始对它进行解释了。

首先我们介绍什么是可迭代对象:

在数据类型的后面可以使用点加双下划线iter(.iter)来判断是不是可迭代对象

经过一圈测试得到如下的可迭代对象的范:

不是可迭代对象:
int float bool 函数对象
是可迭代对象:
str list dict tuple set 文件对象


可迭代对象:
	内置有__iter__方法的都可以称之为是可迭代对象
    '内置': 直接可以使用点出来的
    """
    	__iter__方法的读法
    	后面会有_开头的方法,这种方式也是有特殊含义的,大家最后先不要使用
    """
s.__iter__()
l.__iter__()
t.__iter__()
se.__iter__()
d.__iter__()
# b.__iter
file = open('a.txt', 'w')
file.__iter__()
"""
可迭代对象:
    str、list、dict、set、tuple、文件类型
以上基本数据类型都是可迭代对象
"""
print(s)
'''可迭代对象调用__iter__方法之后就是迭代器对象了'''
print(s.__iter__()) # <str_iterator object at 0x0000023F68F31100>
print(iter(s))

def iter(s):
    return s.__iter__()

print(len(s))
print(s.__len__())

def len(s):
    return s.__len__()
"""
__iter__开头的方法都有一个与之对应的简写:iter(s) 方法名()
"""

迭代器对象

迭代器介绍:

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

迭代器对象:

所有的可迭代对象在使用双下划线iter方法后都会编程迭代器对象,变成迭代器对象后继续使用双下划线iter方法就没有效果了,但是可以执行。判断迭代器对象的本质是查看内置方法中是否有__iter__和__next__。

迭代器对象的作用:

优点

1、提供了一种不依赖于索引取值的方式,正因为有迭代器的存在 我们的字典 集合才能够被for循环

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

缺点

1、除非取尽,否则无法获取迭代器的长度

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

'''迭代器实操'''
s1 = 'hello'  # 可迭代对象
res = s1.__iter__()  # 迭代器对象
print(res.__next__())  # 迭代取值 for循环的本质
# res=s.__iter__() # 迭代器对象
# print(res.__next__())  # next其实就是迭代取值的,而且不依赖于索引取值
# print(res.__next__())
# print(res.__next__())
# print(res.__next__())
# print(res.__next__())
#
# res1 = l.__iter__()
# print(res1.__next__())
# print(res1.__next__())
# print(res1.__next__())
# print(res1.__next__())
# print(res1.__next__())  # 迭代取值,一旦值被取完,就会直接报错

# print(next(res1))
# print(next(res1))
# print(next(res1))
# print(next(res1))

"""
迭代器给我们提供了一种不依赖于索引取值的方式
"""
# 可迭代对象调用多次__iter__方法仍然是迭代器对象
# print(res.__iter__().__iter__().__iter__())
#### 易错题
l = [1, 2, 3, 4]
print(l.__iter__().__next__()) # 1
print(l.__iter__().__next__()) # 1
print(l.__iter__().__next__()) # 1
print(l.__iter__().__next__()) # 1

res = l.__iter__()
print(res.__next__())  # 1
print(res.__next__())  # 2
print(res.__next__())  # 3
print(res.__next__())  # 4   
以为每次执行__iter__之后都是一个从头开始的新迭代器

for循环的内部原理

l = [1, 2, 3, 4, 5, 6, 7, 8, 9] # 循环打印出列表中每一个元素值,但是不能使用for循环

l = [1, 2, 3, 4, 5, 6, 7, 8, 9] # 循环打印出列表中每一个元素值,但是不能使用for循环

# count=0
# while True:
#     print(l[count])
#     count+=1
# res=l.__iter__()
# while True:
#     print(res.__next__())

'''for循环为什么不报错呢'''
# for i in l:
#     print(i)

res=l.__iter__()
while True:
    try:           	异常捕获
        print(res.__next__())
    except:
        break
     
"""
	for循环内部的原理:
		1. 首先把关键字in后面的可迭代对象转为迭代器对象
		2. while循环取值__next__迭代取值,当next取值完毕的时候会报错
		3. 当内部报错的时候,for循环进行了异常捕捉
"""

异常捕捉

一、基础知识

1、什么是异常

当我们写程序难免遇到报错,专业的称呼叫做异常,行业俗语叫做bug,由于异常情况出现会导致代码停止运行,所以在编写过程中要尽可能避免。

2、异常分类

语法错误

这一类错误很好理解,相当于你不会写,比如用if或for的时候不知道他们怎么写,这种情况比较夸张了,好好学过的人都不会犯这种错误,一般来说这种错误发现了就是要准备跑路了。

逻辑错误

这种情况在编写的时候是允许出现的,通常来说都是代码结构完整,但是在调用的时候出了各种错误如:顺序错了或是变量用错了导致的异常。

3、异常的结构

我们可以分成三部分看:

错误位置

报错的位置是占最大部分的,我们从这里知道什么位置运行代码出错了,通常来说在错误位置很多的时候只用看最下面那个。

错误类型

最下面一行左边的几个单词是标志你的错误类型的,通常也不用管,起到提示作用

错误详情

这里比较关键,错误详情会把你错误的地方进行一个具体的解释,好让你找到错误并改正

二、异常常见类型

当我们在编写python代码的时候会出现各种原因导致的异常,这里我们可以举一些常见的例子:

1、AssertionError

当 assert 语句失败时将被引发。用户利用断言语句检测异常时,如果断言语句检测的表达式为假,则会引发这种异常。

2、KeyError

KeyError是关键字错误,当在现有键集合中找不到指定的映射(字典)键时就会引发错误。这个异常主要发生在字典中,比如当用户试图访问一个字典中不存在的键时会被引发。

3、NameError

NameError是当某个局部或全局名称未找到时将被引发,也就是指变量名称发生错误,比如用户试图调用一个还未被赋值或初始化的变量时会被触发。

4、ValueError

当操作或函数接收到具有正确类型但值不适合的参数,也就是值错误,比如想获取一个列表中某个不存在值的索引。

5、SystemError

当解释器发现内部错误,但情况看起来尚未严重到要放弃所有希望时将被引发。 关联的值是一个指明发生了什么问题的字符串(表示为低层级的符号)。

6、SyntaxError

SyntaxError主要是因为当解析器遇到语法错误,比如少个冒号、多个引号之类的,编程时稍微疏忽大意一下就会出错,应该是最常见的一种异常错误了。

7、TypeError

TypeError是类型错误,当一个操作或函数被应用于类型不适当的对象时将被引发。比如在要求 int 时却传入了 list就会导致错误。

8、IndexError

当序列抽取超出范围时将被引发,也就是索引超出范围,比如最常见下标索引超出了序列边界,比如当某个序列m只有三个元素,却试图访问m[4]。

9、StopIteration

StopIteration为迭代器错误,由内置函数 next() 和 iterator 的 __next__() 方法所引发,用来表示该迭代器不能产生下一项。当访问至迭代器最后一个值时仍然继续访问,就会引发这种异常。

10、AttributeError

AttributeError是属性错误,当属性引用或赋值失败时就会出现。比如列表有index方法,而字典却没有,所以对一个字典对象调用该方法就会引发该异常。

11、AttributeError    

访问的对象属性不存在

12、ImportError    

无法导入模块或者对象,主要是路径有误或名称错误    

13、IndentationError 

代码没有正确对齐,主要是缩进错误

14、IOError        

输入/输出异常,主要是无法打开文件

15、OverflowError    

数值运算超出最大限制

16、TabError    

Tab和空格混用

17、ZeroDivisionError    

除法运算中除数0 或者 取模运算中模数为0

三、异常处理语法结构

在python中我们有专门的语法结构来应对一些将要出错的代码:

语法结构:
try:
	待监测的代码(可能会出错的代码)
except 错误类型 as e:
	针对上述错误类型制定的方案
功能讲解:

1、我们也可以根据这里面单词的意思大概看出来这个语法结构的作用,第一个单词在这里可以看成是尝试的意思,相应的我们把需要监测的代码放里面运行,第二个单词是期望的意思,就是说如果代码出现的错误是我们所期望的这个,那么就用下面的子代码进行处理,如果期望类型错了就会报错,对了就不会报错。

2、except后面的as e可以不写,写了的话会返回系统提示的报错信息。

3、当我们使用try监测代码的时候只能监测一个错误,当代码检测到第一个错误的时候就会输出提示信息然后停止代码的运行,后面的错误就不会被检测到。

4、这里我们也发现了,一个except只能应对一种错误类型,如果错误类型很多的话我们有两种选择,一种是写很多个except把错误类型写全,第二种选择是在错误类型处写上Exception/BaseException,就是后面要说的万能异常。

try:
	待监测的代码(可能会出错的代码)
except 错误类型1 as e:  # e就是系统提示的错误信息
	针对上述错误类型1制定的方案
except 错误类型2 as e:  # e就是系统提示的错误信息
	针对上述错误类型2制定的方案
except 错误类型3 as e:  # e就是系统提示的错误信息
	针对上述错误类型3制定的方案
 	 ...
万能异常

对应的异常类型是:Exception/BaseException

两个异常类型作用一样,都包含所有的错误类型,但是BaseException是Exception的父代码。

try:
	待监测的代码(可能会出错的代码)
except Exception as e:  # e就是系统提示的错误信息
	针对各种常见的错误类型全部统一处理
try语法结构结合else和finally

try结构结合else和finall功能很简单:

1、else后面跟的子代码是在try的子代码正常运行的时候才会执行的

2、finally是不管try的子代码运行是否正确都会运行的代码

try:
	待监测的代码(可能会出错的代码)
except Exception as e:  # e就是系统提示的错误信息
	针对各种常见的错误类型全部统一处理
else:
	try的子代码正常运行结束没有任何的报错后 再执行else子代码
finally:
	无论try的子代码是否报错 最后都要执行finally子代码
注意事项

1、虽然异常处理可以防止代码报错停止运行,但是这并不意味着我们可以多用异常处理减少异常。

2、异常处理应该尽量少用,当出现无法控制的情况报错才考虑使用,如手机软件在无法访问网络的时候使用异常处理进行提示现在没网不能运行。

3、try语法结构下监测的代码应该尽量少一些。

四、异常处理补充

1、断言

断言就是对一段代码进行判断,如果判断结果正确就继续运行,如果判断错误了就抛出AssertionError异常,直接中断代码的运行

name = 'jason'
# assert isinstance(name, int)
assert isinstance(name, str)
print('哈哈哈 我就说吧 肯定是字符串')
name.strip()

这里我们用isinstance判断数据的类型,如果把name绑定的字符串换成列表就会中断代码的运行抛出AssertionError异常。

2、主动抛出异常

跟上面的断言原理相似,当我运行代码检测到指定的数据值或条件的时候就直接抛出异常中断代码的运行。主动抛出异常需要用到raise这个方法,或是断言中的assert方法。

raise 异常类型名称(‘异常描述')

name = 'jason'
if name == 'jason':
	raise Exception('老子不干了')
else:
	print('正常走')

当name绑定的值是字符串jason的时候就抛出异常中断代码运行。

五、异常处理实战应用

使用while循环+异常处理+迭代器对象 完成for循环迭代取值的功能

l1 = [11, 22, 33, 44, 55, 66, 77, 88, 99]
die_qi = l1.__iter__()
while True:
    try:
        res = die_qi.__next__()
        print(res)
    except Exception as e:
        print('数据取完了')
        break

这里的Exception错误类型范围太广了,我们换成StopIteration异常类型也是一样的作用。