Python函数装饰器&异常处理

Python函数装饰器&异常处理

一,函数装饰器

1.1 函数装饰器应用场景

​ 大家有没有听说过,开发和产品经理的故事。这两个物种凑合到一起,是火药味十足的。老有产品经理对开发说,卧槽!你这写得啥玩意儿??太low 了,你在执行前执行一个什么,执行以后再做一些什么操作。经常提需求,也经常改需求。对于我们开发来说,要是只有一个函数来说,我们改改还好,但是如果涉及到了100个函数,那这100个都需要改。

​ 比如:支付功能涉及到100个函数,今天产品经理过来说,每个函数需要加一个验证用户登录得功能。那这100个函数,我们都得一个一个点开改!这他娘得不加班才怪呢!

def func1():
    print("func1")


def func2():
    print("func2")


def func3():
    print("func3")


"""
    下面有一万个函数
"""

func1()
func2()
func3()

​ 现在产品经理过来拍了一下肩膀,“攀攀哇,我这有个简单得功能,你这么屌!一会儿就改完了。你把这一万个函数,都加一个验证用户功能呀”。卧槽,我当时心里一万只“草泥马,奔腾而过。加就加呗,我一个个点开函数加起来了,于是乎,经过我连续一个周得通宵加班,给他搞完了,如下:

def func1():
    print("验证用户...")
    print("func1")


def func2():
    print("验证用户...")
    print("func2")


def func3():
    print("验证用户...")
    print("func3")


"""
    下面有一万个函数
"""

func1()
func2()
func3()

​ 上线后,产品经理又走过来”攀攀哇,这个验证功能,我想了一下,用户体验度不好,咱还是给他删了吧!咦?你不会不想删吧?我今天日报刚好没什么跟老板汇报得,这个我可以汇报一下” 都这么说了,还能咋搞,硬着头皮改呗,于是乎连续一个周得通宵加班给删掉了。没过几天,产品经理又走过来,“攀攀哇,我想了一下,我修改了一下验证得逻辑,你再加上,这回一定行”,得!又得连续一个周得通宵加班~~当然连续三周通宵加班,我可能会猝死!!

​ 当然,这个案例很夸张,大家不要慌。但是实际开发中,这种频繁修改需求是比较常见的。我们如何有效的解决这个问题,那么函数的装饰器,作用就体现出来了。

1.2 函数装饰器的写法

函数装饰器本质上是一个函数。

def outer(fun):
    def wrapper():
        print("支付之前:验证用户....")
        fun()
        print("支付之后: 谢谢老板....")

    return wrapper

@outer
def func1():
    print("执行:func1")

@outer
def func2():
    print("执行:func2")

@outer
def func3():
    print("执行:func3")


"""
    下面有一万个函数
"""

func1()
func2()
func3()

​ @outer这个注解,是让func1(),func2(),func3() 这些函数,与我们新写的outer()函数勾搭起来。在这里面outer()函数,就是func1,func2,func3 这些函数的装饰器。

1.3 当调用func1()函数之后执行步骤(debug模式看执行流程):

首先要注意,装饰器实际上是一个函数。

  1. 在需要调用的函数上面加上@装饰器函数名,比如@outer ,让装饰器和函数勾搭起来(建立连接)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SbL1PNiY-1639397096253)(补充:函数装饰器&异常处理.assets/image-20211118155922983.png)]

  2. 将调用的函数,绑定到装饰器函数的参数里面,比如上面的案例中,outer(fun) ,实际上fun = func1()

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CjcWTXgs-1639397096253)(补充:函数装饰器&异常处理.assets/image-20211118160013671.png)]

  3. 加载装饰器函数中的内部函数wrapper()函数,并返回wrapper函数

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iEjInKKt-1639397096254)(补充:函数装饰器&异常处理.assets/image-20211118160113511.png)]

  4. 此时原本调用的函数,会被返回的wrapper()函数替代。比如上面调用func1()最后会被修改成wrapper()函数

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FQ4lF4KG-1639397096254)(补充:函数装饰器&异常处理.assets/image-20211118160152619.png)]

  5. 执行装饰器中的wrapper函数

1.4 带参数函数的装饰器
def outer(fun):
    def wrapper(name):
        print("支付之前:验证用户....")
        fun(name)
        print("支付之后: 谢谢老板....")
    return wrapper

def func1(arg):
    print("func1....",arg)

func1("攀哥...")
1.5 带返回值函数的装饰器
def outer(fun):
    def wrapper(name):
        print("支付之前:验证用户....")
        result = fun(name)
        print("支付之后: 谢谢老板....")
        return result

    return wrapper


def func1(name):
    print("func1....", name)
    return "%s真帅" % name


result =  func1("攀哥...")
print(result)
1.6 作业:
1. 编写一个统计一个字符串中有多少个字符,和每一个字符出现多少次的函数
2. 为这个函数编写一个装饰器,用来计算执行这个函数需要多长时间
3. 提示: 获取系统当前的毫秒数,导入timer模块  然后使用:timer.timer()

二,异常处理

2.1 什么是异常

什么叫异常?超出逾期的。比如:

在开发过程中,切记,一定不要让用户看到系统出错的信息。

2.2 常见的异常
异常名称 描述
BaseException 所有异常的基类
SystemExit 解释器请求退出
KeyboardInterrupt 用户中断执行(通常是输入^C)
Exception 常规错误的基类
StopIteration 迭代器没有更多的值
GeneratorExit 生成器(generator)发生异常来通知退出
StandardError 所有的内建标准异常的基类
ArithmeticError 所有数值计算错误的基类
FloatingPointError 浮点计算错误
OverflowError 数值运算超出最大限制
ZeroDivisionError 除(或取模)零 (所有数据类型)
AssertionError 断言语句失败
AttributeError 对象没有这个属性
EOFError 没有内建输入,到达EOF 标记
EnvironmentError 操作系统错误的基类
IOError 输入/输出操作失败
OSError 操作系统错误
WindowsError 系统调用失败
ImportError 导入模块/对象失败
LookupError 无效数据查询的基类
IndexError 序列中没有此索引(index)
KeyError 映射中没有这个键
MemoryError 内存溢出错误(对于Python 解释器不是致命的)
NameError 未声明/初始化对象 (没有属性)
UnboundLocalError 访问未初始化的本地变量
ReferenceError 弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError 一般的运行时错误
NotImplementedError 尚未实现的方法
SyntaxError Python 语法错误
IndentationError 缩进错误
TabError Tab 和空格混用
SystemError 一般的解释器系统错误
TypeError 对类型无效的操作
ValueError 传入无效的参数
UnicodeError Unicode 相关的错误
UnicodeDecodeError Unicode 解码时的错误
UnicodeEncodeError Unicode 编码时错误
UnicodeTranslateError Unicode 转换时错误
Warning 警告的基类
DeprecationWarning 关于被弃用的特征的警告
FutureWarning 关于构造将来语义会有改变的警告
OverflowWarning 旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning 关于特性将会被废弃的警告
RuntimeWarning 可疑的运行时行为(runtime behavior)的警告
SyntaxWarning 可疑的语法的警告
UserWarning 用户代码生成的警告
2.3 异常的使用场景

比如,在除法运算的时候,我们知道除数不能为0.那么在程序中如果除数为0了,就会出现异常

num1 = int(input("请输入第一个数:"))
num2 = int(input("请输入第二个数:"))
print(num1/num2)
print(num1)
print(num2)
请输入第一个数:40
请输入第二个数:0
Traceback (most recent call last):
  File "D:\py_work\study\basic\异常.py", line 4, in <module>
    print(num1/num2)
ZeroDivisionError: division by zero

Process finished with exit code 1

​ 我们会发现,当num2为0的时候,就会出现ZeroDivisionError: division by zero 异常信息。并且程序这个时候会终端,也就是后面的两行打印语句没有打印。

​ 在开发中,为了用户的体验,避免用户看到爆红的错误信息,同时也为了系统的安全性,我们会屏蔽掉这个错误信息。这个时候我们需要捕捉并处理一下异常。

2.4 捕捉和处理异常的方式

捕捉异常可以使用 try/except语句。

try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。

如果你不想在异常发生时结束你的程序,只需在try里捕获它。

格式:

try:
<语句>        #运行别的代码
except <名字><语句>        #如果在try部份引发了'name'异常
except <名字><数据>:
<语句>        #如果引发了'name'异常,获得附加的数据
else:
<语句>        #如果没有异常发生

看如下代码:

try:
    num1 = int(input("请输入第一个数:"))
    num2 = int(input("请输入第二个数:"))
    print(num1/num2)
except ZeroDivisionError:
    print("哥们儿!除数不为零?懂?")
else:
    print("nice!我马上为你计算")

print(num1)
print(num2)

运行效果:

请输入第一个数:40
请输入第二个数:0
哥们儿!除数不为零?懂?
40
0

​ 上面代码中,except ZeroDivisionError 表示捕捉除数为0的异常,如果num2=0,就会出现除数为0的异常,会被except捕捉到。然后执行except里面的代码。

​ 如果num2不为0,就没有异常,这个时候可以执行else中的代码。

​ 注意:else 不是必须的。可以不写!!

try 语句按照如下方式工作;

  • 首先,执行 try 子句(在关键字 try 和关键字 except 之间的语句)。
  • 如果没有异常发生,忽略 except 子句,try 子句执行后结束。
  • 如果在执行 try 子句的过程中发生了异常,那么 try 子句余下的部分将被忽略。如果异常的类型和 except 之后的名称相符,那么对应的 except 子句将被执行。
  • 如果一个异常没有与任何的 except 匹配,那么这个异常将会传递给上层的 try 中。
2.5 使用except而不带任何异常类型
try:
    num1 = int(input("请输入第一个数:"))
    num2 = int(input("请输入第二个数:"))
    print(num1/num2)
except :
    print("哥们儿!除数不为零?懂?")
else:
    print("nice!我马上为你计算")

print(num1)
print(num2)

​ 以上方式try-except语句捕获所有发生的异常。但这不是一个很好的方式,我们不能通过该程序识别出具体的异常信息。因为它捕获所有的异常。

2.6 如何同时捕获多个异常

格式1:

try:
   ....
except 异常1:
   ....
except 异常2:
   ....
else:
   ....
wife_list = ["赵丽颖","张天爱","宵鱼鱼","戴雨彤"]
try:
    num1 = int(input("请输入第一个数:"))
    num2 = int(input("请输入第二个数:"))
    print(num1/num2)
    for x in range(len(wife_list)):
        print(x,".",wife_list[x])
    index = int(input("请输入今晚要翻牌的编号"))
    wife = wife_list[index]
    print("你今天选的是:",wife)
except ZeroDivisionError:
    print("哥们儿!除数不为零?懂?")
except IndexError:
    print("哥们儿!醒醒你没有这个老婆")
else:
    print("nice!我马上为你安排")

格式2:

try:
    正常的操作
   ......................
except(Exception1[, Exception2[,...ExceptionN]]):
   发生以上多个异常中的一个,执行这块代码
   ......................
else:
    如果没有异常执行这块代码

例如:

wife_list = ["赵丽颖","张天爱","宵鱼鱼","戴雨彤"]
try:
    num1 = int(input("请输入第一个数:"))
    num2 = int(input("请输入第二个数:"))
    print(num1/num2)
    for x in range(len(wife_list)):
        print(x,".",wife_list[x])
    index = int(input("请输入今晚要翻牌的编号"))
    wife = wife_list[index]
    print("你今天选的是:",wife)
except (ZeroDivisionError,IndexError):
    print("数据输入有误~~")
else:
    print("这就给你安排")

​ 当num2=0的时候,会发生除数为零的异常,会被except ZeroDivisionError: 捕获。如果选取了wife_list列表不存在的索引时,就会被except IndexError:捕获。如果没有发生异常,就会执行else中的语句

2.7 try-finally 语句

try-finally 语句无论是否发生异常都将执行最后的代码。

wife_list = ["赵丽颖","张天爱","宵鱼鱼","戴雨彤"]
try:
    num1 = int(input("请输入第一个数:"))
    num2 = int(input("请输入第二个数:"))
    print(num1/num2)
    for x in range(len(wife_list)):
        print(x,".",wife_list[x])
    index = int(input("请输入今晚要翻牌的编号"))
    wife = wife_list[index]
    print("你今天选的是:",wife)
except ZeroDivisionError:
    print("哥们儿!除数不为零?懂?")
except IndexError:
    print("哥们儿!醒醒你没有这个老婆")
else:
    print("这就给你安排")
finally:
    print("龙仔竭诚为你服务")

注意: else 和finally 不是必须得加得,根据实际情况而定

2.8 打印异常信息

一个异常可以带上参数,可作为输出的异常信息参数。

你可以通过except语句来捕获异常的参数,如下所示:

try:
    正常的操作
   ......................
except ExceptionType as Argument:
    你可以在这输出 Argument 的值...

比如:

while True:
    try:
        x = int(input("请输入一个数字: "))
        break
    except ValueError as e:
        print(e)
        print("您输入的不是数字,请再次尝试输入!")
2.9 抛出异常

Python 使用 raise 语句主动抛出一个指定的异常。

raise语法格式如下:

raise Exception("异常信息")

比如:我们一般在注册账号的时候要求密码必须是6-18位,如果用户输入错误,我们需要抛出异常

while True:
    str = input("请输入6-18位密码")
    if len(str)<6 or len(str)>18:
        raise Exception("密码必须时6-18位")

你可能感兴趣的:(Python笔记,python,开发语言,后端)