第七章 异常,枚举和装饰器
day7.异常,枚举与装饰器
1.异常
1.1.什么是异常
异常是错误发生的信号,一旦程序出错就会产生一个异常,如果该异常
没有被应用程序处理,那么该异常就会抛出来,程序的执行也随之终止
异常包含三个部分
1. 异常的追踪信息 File "D:\python代码A8\day6异常和购物车\1.异常.py", line 8, in <module>
2. 异常的类型 NameError
3. 异常的信息 name 'a' is not defined
a = 1
if a
# SyntaxError
aaa
# NameError
d = {'x':1,'y':2}
d['z']
# KeyError
L = [1,2]
L[2]
# IndexError
错误分为两大类
1. 语法上的错误:在程序运行前就应该立即修正 这个好避免
2. 逻辑上的错误:比如:字典没有key,非要取key
列表没有索引
变量没有名字 这些不好避免
1.2.为何要异常处理
避免程序因为异常而崩溃,所以在应用程序中应该对异常进行处理,从而增强程序的健壮性
1.3.如何异常处理
try:
代码1
代码2
代码3
......
except NameError:
当抛出的异常是NameError时执行的子代码块
except ....:
pass
else:
pass
finally:
pass
1.4.异常处理的单分支
try:
a =1
except NameError as f:
print('兄弟检查一下你的代码')
print(f)
else:
# # 程序没有抛出异常的时候执行
print('代码正确')
finally:
# # 不管有错没错 一定会执行的部分
print('完成了异常捕获')
print('aaaaaaaaa')
1.5.异常处理的多分支
try:
L = [1, 2]
L[2]
print('===========')
print('===========')
print('===========')
a
except IndexError as f:
# 一但捕获到了异常
print('兄弟检查一下你的代码')
print(f)
except NameError as f:
# 一但捕获到了异常
print('兄弟检查一下你的代码')
print(f)
else:
# # 程序没有抛出异常的时候执行
print('代码正确')
finally:
# # 不管有错没错 一定会执行的部分
print('完成了异常捕获')
# print('aaaaaaaaa')
1.6.合并一下多个异常
try:
L = [1, 2]
a
print('===========')
print('===========')
print('===========')
L[2]
except (IndexError,NameError) as f:
# 一但捕获到了异常
print('兄弟检查一下你的代码')
print(f)
else:
# # 程序没有抛出异常的时候执行
print('代码正确')
finally:
# # 不管有错没错 一定会执行的部分
print('完成了异常捕获')
# print('aaaaaaaaa')
1.7.万能捕获异常:Exception
# 万能捕获 Exception
try:
d = {'x':1,'y':2}
d['z']
L = [1, 2]
L[2]
# 结束运行了
print('===========')
print('===========')
print('===========')
# 不会运行
a
except Exception as f:
# 一但捕获到了异常
print('兄弟检查一下你的代码')
print(f)
else:
# # 程序没有抛出异常的时候执行
print('代码正确')
finally:
# # 不管有错没错 一定会执行的部分
print('完成了异常捕获')
# print('aaaaaaaaa')
1.8.自定义异常
# 自定义异常
L = [1,2,3,4,5]
print(len(L))
# # # # 满足条件抛出异常
if len(L) != 5:
raise TypeError('列表的长度必须为5,这个是我的规则')
# # 第二种方式 # 断言
# assert len(L) == 5
1.9.筛选列表里面的字典key带有sex的字典
info =[
{'name':'dahai','age':18,'sex':'男'},
{'name':'dahai1','age':24,'sex':'男'},
{'name':'xialuo','age':78},
{'name':'dahai2','age':27,'sex':'女'},
{'name':'xishi','age':8}
]
for i in info:
try:
i['sex']
except Exception as f:
# print(f)
# 可以筛选出没有sex这个key的字典
print(i)
for i in info:
try:
# 可以筛选出带有sex这个key的字典
i['sex']
print(i)
except KeyError as f:
# print(f)
# 可以筛选出没有sex这个key的字典
# print(i)
# print(f)
pass
2.枚举
2.1.与for循环
# @Author : 大海
# @File : 枚举.py
goods_list = [ '大海','夏洛','顾安']
# 枚举 一个是索引号 一个是值
for i,item in enumerate(goods_list):
print(i)
print(item)
2.2.结合列表生成式
# print([index for index, item in enumerate([3, 5, 7, 3, 7])])
# print([item for index, item in enumerate({'name':'dahai','age':18})])
#
# for index, item in enumerate({'name':'dahai','age':18}):
# print(index,item)
# 被3整除的列表数据 获得是对应的索引
print([index for index, item in enumerate([3, 5, 7, 3, 7]) if item % 3==0])
# 被2整除的列表索引 获得是对应的索引
print([index for index, item in enumerate([3, 5, 7, 3, 7]) if index % 2==0])
# 被3整除的列表数据 获得是对应的数据
print([item for index, item in enumerate([3, 5, 7, 3, 7]) if item % 3==0])
2.3.结合字典生成式
# 字典生成式
print({index:item for index, item in enumerate([3, 5, 7, 3, 7])})
print({str(index):item for index, item in enumerate([3, 5, 7, 3, 7])})
# 被2整除的列表索引 获得是对应的索引和列表数据组合的键值对字典
print({str(index):item for index, item in enumerate([3, 5, 7, 3, 7]) if index % 2==0})
# 被3整除的列表数据 获得是对应的索引和列表数据组合的键值对字典
print({str(index):item for index, item in enumerate([3, 5, 7, 3, 7]) if item % 3==0})
3.装饰器
3.1.什么是装饰器
(就是一个函数,只不过这个函数不是给自己使用的,是给其他函数添加功能的)
器指的是工具,而程序中的函数就具备某一功能的工具
装饰指的是为被装饰器对象添加额外功能
3.2.为什么要用装饰器
软件的维护应该遵循开放封闭原则
开放封闭原则指的是:
软件一旦上线运行后对修改源代码是封闭的,对扩展功能的是开放的
这就用到了装饰器
装饰器的实现必须遵循两大原则:
1、不修改被装饰对象的源代码(人的原来的性格,生活方式)
2、不修改被装饰对象的调用方式(人的原来的外貌,名字)
错误例子
def run():
print('跑步')
print('健身')
run()
def fitness():
print('健身')
run()
def run():
print('跑步')
fitness()
3.3.装饰器的实现必须遵循两大原则
1、不修改被装饰对象的源代码(人的原来的性格,生活方式)
2、不修改被装饰对象的调用方式(人的原来的外貌,名字)
装饰器其实就在遵循1和2原则的前提下为被装饰对象添加新功能
形象比喻
比如男孩1给女朋友买衣服,项链或化妆品 , 变的更加自信,原来的外貌,原来的性格没有发生改变 # 装饰器
比如男孩2带女朋友去整容 , 变的更加自信(不一定), 原来的外貌,原来的性格发生改变 # 不是装饰器
女朋友
3.4.装饰器是实现方式实际上是:
函数对象+闭包函数的组合使用的产物
3.4.1.函数对象的使用
函数对象可以被当作数据处理
# 变量引用数据: 数据类型 引用另一个变量 引用一个函数对象(函数内存地址)
# 函数对象可以被当作数据处理
# 函数对象 加括号(有参或者无参)调用执行函数
1.引用
# 函数可以被当作数据处理
def func():# <function func at 0x01F199B8>
print('from func')
print(func)
x='hello'
# 1.引用
y=x
f=func
print(f)
f()
2.当作参数传给一个函数
x='hello'
def func():# <function func at 0x01F199B8>
print('from func')
len(x)
def foo(m):
print(m)
m()
foo(func)
3.可以当作函数的返回值
x='hello'
def func():# <function func at 0x01F199B8>
print('from func')
def foo(x): # x=func
return x # return func
res=foo(func)
print(res)
res()
4.可以当作容器类型的元素
x='hello'
def func():# <function func at 0x01F199B8>
print('from func')
l=[x,]
print(l)
l=[func,]
print(l)
l[0]()
例子
def pay():
print('支付。。。')
def withdraw():
print('取款。。。')
def transfer():
print('转账。。。')
def check_balance():
print('查看余额。。。')
def shopping():
print('购物。。。')
func_dic={
'1':pay,
'2':withdraw,
'3':transfer,
'4':check_balance,
'5':shopping
}
while True:
msg="""
1 支付
2 取款
3 转账
4 查看余额
5 购物
6 退出
"""
print(msg)
choice=input('>>: ').strip()
if choice == '6':
break
if choice not in func_dic:
print('输入的指令不存在')
continue
func_dic[choice]()
3.4.2.回顾闭包函数
1.闭包的概念
闭指的是:该函数是一个内部函数
包指的是:指的是该内部的函数名字在外部被引用
2.构成条件:
1.函数嵌套前提
2.外部函数返回内部函数名
3.内部函数使用外部函数的变量
3.闭包获得内层函数的内存地址
def outer():
print('外面的函数正在运行')
def inner():
print('里面的函数正在运行')
return inner
inner=outer()
inner()
4.闭包获得内层返回值,该返回值是外层函数内的变量名
外层函数内的变量名被称为:非全局变量,也叫做自由变量
这个自由变量会和内层函数形成一种绑定关系
自由变量不会和全局变量产生冲突,所以保证了数据安全
def outer():
# 自由变量
name = '大海'
print('外面的函数正在运行')
def inner():
print('里面的函数正在运行')
return name
return inner
inner=outer()
inner()
3.4.3.装饰器的结构
装饰器其实是在闭包的基础上,对闭包的外层函数传入被装饰函数对象
1.被装饰的无参函数
def run():
print('============')
print('我是大海')
print('============')
我们要实现的装饰该函数的效果
# 把一个整体综合的知识点拆解开来理解 能力的提升
def run():
print('============')
print('我是大海')
print('============')
print('我是被装饰函数前面的代码')
run()
print('我是被装饰函数后面的代码')
2.装饰器装饰无参函数
def run():
print('============')
print('我是大海')
print('============')
2.1.装饰器的定义阶段
# # # 装饰器就是一个特殊的闭包函数
# 1.定义了decorate,检测decorate语法, new_func没有定义
def decorate(func):# 传入我们被装饰的函数对象(也就是函数的地址)
# print(func)# run
# 等下我们是要运行这个new_func这个函数
def new_func():
print('我是被装饰函数前面的代码')
func()# 调用被装饰的函数
print('我是被装饰函数后面的代码')
return new_func
2.2.装饰器的调用阶段
# 2.调用decorate,定义new_func,和返回这个函数new_func的地址
run=decorate(run)
print(run) # <function decorate.<locals>.new_func at 0x019598E0>
run()# 这个run实际上是new_func
3.装饰器装饰有参函数
name = '大海'
def run(name):
print('============')
print('我是%s'%name)
print('============')
# run(name)
# # # 装饰器就是一个特殊的闭包函数
# # 1.定义了decorate,检测decorate语法, new_func没有定义
def decorate(func):# 传入我们被装饰的函数对象(函数的地址)
# print(func)# run
# 等下我们是要运行这个new_func这个函数
def new_func(new_name):
print('我是被装饰函数前面的代码')
func(new_name)
print('我是被装饰函数后面的代码')
return new_func
# 定义new_func,和返回这个函数new_func的地址,
# 调用decorate
run=decorate(run)
run(name)# 参数实际上是传给了new_func这个函数
但是它只能装饰器一个位置参数的函数
所以new_func参数应该写成*args,**kwargs,这样无论是有参函数无参都能装饰
def run_dahai():
print('============')
print('我是大海')
print('============')
name = '夏洛'
def run_xialuo(name):
print('============')
print('我是%s'%name)
print('============')
# # # 装饰器就是一个特殊的闭包函数
# # 1.定义了decorate,检测decorate语法, new_func没有定义
def decorate(func):# 传入我们被装饰的函数对象(函数的地址)
# print(func)# run
# 等下我们是要运行这个new_func这个函数
def new_func(*args,**kwargs):# 被装饰的函数有没有参数都可以,按照被装饰的函数传参规则即可
print('我是被装饰函数前面的代码')
func(*args,**kwargs)
print('我是被装饰函数后面的代码')
return new_func
# 定义new_func,和返回这个函数new_func的地址,
# 调用decorate
run_dahai=decorate(run_dahai)
run_dahai()
run_xialuo=decorate(run_xialuo)
run_xialuo(name)
4.时间装饰器
了解一下datetime模块(后面模块那节课细讲)
1.拿到当前时间
from datetime import datetime
# 当前时间
print(datetime.now())
2.测试for循环从1加到9000000的时间
实现效果
# 记得导入datetime时间模块
from datetime import datetime
n = 9000000
def for1(n):
sum1 = 0
for i in range(1,n+1):
# print(i)
sum1 += i
print(sum1)
start_time = datetime.now()
print('开始时间%s' % start_time)
for1(n)
end_time = datetime.now()
print('结束时间%s' % end_time)
time1 = end_time - start_time
print('花费时间%s' % time1)
5.时间装饰器装饰for1
# # # # 时间装饰器
from datetime import datetime
n = 9000000
def for1(n):
sum1 = 0
for i in range(1,n+1):
# print(i)
sum1 += i
print(sum1)
for1(n)
#
# # 一.定义了run_time,检测run_time语法, new_func没有定义
def run_time(func):# func 是for1 这是一个用来计算程序执行时间的装饰器
# print(func)
def new_func(*args,**kwargs):# for1(n) 的n
start_time = datetime.now()
print('开始时间%s' % start_time)
func(*args,**kwargs)
end_time = datetime.now()
print('结束时间%s' % end_time)
time1 = end_time - start_time
print('花费时间%s' % time1)
return new_func
# 二.1.传入了一个for1函数名 2.定义了new_func(n)函数 ,3.返回 了new_func内存地址
for1=run_time(for1)
# print(for1)
for1(n)
# 1.传参 函数名传参 细节
# 2.new_func里面这个函数的参数 是被装饰函数的参数
# 3.return new_func 里面的这个函数
# 4.run_time(for1) 的返回值 是 for1这个名字 但实际上是new_func 函数名
6.简洁的写法:@装饰器名字
为了简洁而优雅地使用装饰器,python提供了专门的装饰器语法来取代for1=run_time(for1)的形式,
需要在被装饰对象的正上方单独一行添加@run_time,产生的效果和for1=run_time(for1)一样,实际上
for1=run_time(for1)是@run_time的底层原理。
for1=run_time(for1):是先定义被装饰的函数和装饰器
@run_time:是先定义装饰器,直接@run_time放到被装饰函数上面就ok。
# # 被装饰的函数放装饰器后面
# # 装饰器定义好直接用@ + 装饰器名字 就可以给被装饰函数进行装饰
# # 测试for循环从1加到9000000的时间
from datetime import datetime
# 当前时间
# print(datetime.now())
# # # 时间装饰器
n = 9000000
# for1(n)
# 一.定义了run_time,检测run_time语法, new_func没有定义
def run_time(func):# func 是for1 这是一个用来计算程序执行时间的装饰器
# print(func)
def new_func(*args,**kwargs):# for1(n) 的n
start_time = datetime.now()
print('开始时间%s' % start_time)
func(*args,**kwargs)
end_time = datetime.now()
print('结束时间%s' % end_time)
time1 = end_time - start_time
print('花费时间%s' % time1)
return new_func
# 二.1.传入了一个for1函数名 2.定义了new_func(n)函数 ,3.返回 了new_func内存地址
@run_time
# # 底层
# # # 一 1.定义了new_func(n)函数 ,2.返回 了new_func内存地址 3.传入了一个for1函数名 4.返回值new_func函数名
# # for1=run_time(for1)
def for1(n):
sum1 = 0
for i in range(1,n+1):
# print(i)
sum1 += i
print(sum1)
for1(n)
@run_time
# shopping=run_time(shopping)
def shopping(n):
for i in range(111111):
pass
print('shopping')
shopping(1)
@run_time
def ccc(n):
for i in range(111111):
pass
print('ccc')
ccc(1)
# 装饰器的要求高于闭包
# 1、不修改被装饰对象的源代码(人的原来的性格,生活方式)
# 2、不修改被装饰对象的调用方式(人的原来的外貌,名字)
# 装饰器 >> 闭包函数 >> 函数定义和调用
语法
'''
def 装饰器外层函数(被装饰的函数):
def 装饰器内层函数(被装饰函数的参数):
被装饰的代码
被装饰的函数(被装饰函数的参数)
被装饰的代码
return 装饰器内层函数
@装饰器外层函数
def 被装饰的函数:
被装饰函数体代码
'''
分类:
一 python 核心基础知识
标签:
python基础
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通