python基础09-异常、模块

异常、模块

一、捕获异常

1. 处理异常目的

  • 只要解释器检查到异常错误,默认执行的动作是终止程序
  • 处理异常目的:防止程序退出,保证程序正常执行

2. 捕获异常

2.1 try…except…

语法格式:

try:
    可能发生异常的代码
except:
      # 处理异常的代码
    1. 如果try里面发生异常
    2. 自动跳转到except里面
  • 把可能出现问题的代码,放在try中
  • 把处理异常的代码,放在except中
  • except后面没有指定异常类型,可以捕获任意类型的异常

示例代码:

try:
    print('=' * 20)
    open('xxx.txt', 'r')  # 发生了异常,自动跳转到except里面
    print('=' * 20)
except:
    print('try里面发生了异常')

运行结果:

====================
try里面发生了异常

2.2 捕获指定异常类型

语法格式:

try:
    可能发生异常的代码
except 异常类型:
      处理异常的代码

示例代码:

try:
    print('=' * 20)
    open('xxx.txt', 'r')  # 发生了异常,自动跳转到except里面
    print('=' * 20)
except FileNotFoundError:
    print('try里面发生了异常')

运行结果:

====================
try里面发生了异常

2.3 except捕获多个异常

语法格式:

try:
    可能发生异常的代码
except (异常类型1, 异常类型2):
      处理异常的代码

示例代码:

try:
    print('=' * 20)
    # open('xxx.txt', 'r')
    print('=' * 20)

    print(num)
    print('=' * 20)
except (FileNotFoundError, NameError):
    print('try里面发生了异常')

2.4 获取异常的信息描述

语法格式:

"""
try:
    可能发生异常的代码
except 异常类型 as 异常对象名:
    print(异常对象名) 即可获取异常的信息描述
"""

示例代码:

try:
    print('=' * 20)
    open('xxx.txt', 'r')
    print('=' * 20)
except FileNotFoundError as e:
    print('异常信息为:', e)

运行结果:

====================
异常信息为: [Errno 2] No such file or directory: 'xxx.txt'

2.5 捕获任意类型的异常

语法格式:

"""
try:
    可能发生异常的代码
except Exception as 异常对象名:
    Exception 为异常类的父类
"""

示例代码:

try:
    print('=' * 20)
    open('xxx.txt', 'r')
    print('=' * 20)
# except Exception: 捕获任意异常的类型
except Exception as e:
    print('异常信息为:', e)

2.6 异常中else

  • 在 if 中,它的作用是当条件不满足时执行的实行
  • 同样在 try…except… 中也是如此,即如果没有捕获到异常,那么就执行else中的事情

语法格式:

"""
try:
    可能发生异常的代码
except:
    处理异常的代码
else:
    没有发生异常,except不满足执行else
"""

示例代码:

try:
    num = 100
   print(num)
except NameError as errorMsg:
    print('产生错误了:%s'%errorMsg)
else:
    print('没有捕获到异常,真高兴')

运行结果:

====================
666
====================
没有发生异常,很开心

2.7 try…finally…

2.7.1 语法格式

语法格式:

"""
try:
    可能发生异常的代码
except:
    处理异常的代码
else:
    没有发生异常,except不满足执行else
finally:
    不管有没有异常,最终都要执行
"""

示例代码:

try:
    print('=' * 20)
    # num = 666
    print(num)
    print('=' * 20)
except Exception as e:
    print('异常信息为:', e)
else:
    print('没有发生异常,很开心')
finally:
    print('不管有没有异常,最终都要执行')

运行结果:

====================
异常信息为: name 'num' is not defined
不管有没有异常,最终都要执行
2.7.2 应用场景
  • 对于文件操作,在文件打开的前提下,后面文件的其它操作,不管有没有发生异常,最终都应该关闭文件
f = open('yyy.txt', 'w')  # 前提是,成功打开文件

try:
    # ret = f.read()
    # print(ret)
    f.write('hello mike')
    print('='*20)
except Exception as e:
    print('产出异常,异常信息为:', e)
else:
    print('没有产生异常')
finally:
    print('不管有没有异常,都要关闭文件')
    f.close()

二、异常传递

1. 异常传递特点

  • 如果异常在内部产生,如果内部不捕获处理,这个异常会向外部传递

2. 异常嵌套

  • try嵌套时,如果内层try没有捕获处理该异常,就会向外层try进行传递
try:
    f = open('yyy.txt', 'w')

    # 内部语句执行完,才向外部传递异常
    try:
        # 前面只写方式打开文件,不能读文件,产生异常
        # 内部没有捕获处理异常
        ret = f.read()
        print(ret)
    finally:
        print('关闭文件')
        f.close()

except Exception as e:
    print('外层捕获异常:', e)

运行结果:

关闭文件
外层捕获异常: not readable

3. 函数嵌套

  • 函数嵌套时,如果内层函数没有捕获处理该异常,就会向外层函数进行传递
# 定义1个函数,函数内部发生了异常 test01(),没有捕获处理
def test01():
    print('开始执行test0111111')
    print(num)
    print('结束执行test0111111')


# 定义另外一个函数 test02, 在函数内部调用test01
def test02():
    print('开始执行test02222222')
    test01()
    print('结束执行test02222222')


# 定义一个test03函数,函数内部调用test01,但是对test01做异常处理
def test03():
    print('开始执行test0333333')

    try:
        test01()
    except Exception as e:
        print('外层函数捕获异常:', e)

    print('结束执行test0333333')


# 调用test02()
# test02()
test03()

运行结果:

开始执行test0333333
开始执行test0111111
外层函数捕获异常: name 'num' is not defined
结束执行test0333333

三、抛出自定义的异常

1. 抛出自定义的异常

  • 用户可用 raise语句 来人为抛出一个异常。
  • 异常/错误对象必须有一个名字,且它们应是Exception类的子类

语法格式:

# 1. 自定义异常类
class 自定义异常类名字(Exception):
    1.1 重新写__init__(self, 形参1, 形参2,……)
        # 建议调用父类的init,先做父类的初始化工作
        super().__init__()
        咱们自己写的代码

    1.2 重新写__str__(),返回提示信息

# 2. 抛出异常类
raise 自定义异常类名字(实参1, 实参2,……)

示例代码:

"""
需求:
1. 自定义异常类,电话号码长度异常类
    1.1 __init__,添加2个属性,用户电话的长度,要求的长度
    1.2 __str__ 返回提示描述意思,如:用户电话长度为:xx位, 这边要求长度为:11位

2. 只要用户输入的手机号码不为11位,抛出自定义异常类
"""


# 1. 自定义异常类,电话号码长度异常类
class NumberError(Exception):
    """自定义异常类,电话号码长度异常类"""

    # 添加2个属性,用户电话的长度,要求的长度
    def __init__(self, _user_len, _match_len=11):
        super().__init__()  # 调用父类的init
        self.user_len = _user_len  # 用户电话的长度
        self.match_len = _match_len  # 要求号码的长度

    def __str__(self):
        return f'用户电话长度为:{self.user_len} 位, 这边要求的长度为:{self.match_len} 位'


# 2. 只要用户输入的手机号码不为11位,抛出自定义异常类
try:
    num_str = input('请输入你的号码:')
    if num_str != 11:
        raise NumberError(len(num_str))  # 抛出自定义异常类
except NumberError as e:  # e 为 NumberError(len(num_str))实例对象 的别名
    print('异常信息为:', e)

运行结果:

请输入你的号码:11232
异常信息为: 用户电话长度为:5 位, 这边要求的长度为:11 位

四、模块

1. 模块介绍

  • 模块是一个由Python代码组成的文件,就是一个以.py结尾的文件。
  • 模块包含函数、类和变量,还可以包括可运行的代码。
  • 模块的主要作用:
    • 提高了代码的可维护性
    • 一个模块编写完毕之后,其他模块直接调用,不用再从零开始写代码了,节约了工作时间
    • 避免名字冲突

2. 模块的导入

2.1 import

  • import导入模块,把整个模块都加载进来

语法格式:

"""
导入格式:     import 模块名
使用格式:     模块名.函数  模块名.类名  模块名.变量名
"""

示例代码:

# 导入模块
import random

# 模块名.函数
num = random.randint(1, 3)
print(num)

# 模块名.类名
# 创建对象
ran = random.Random()
print(type(ran))

# 模块名.变量名
print(random.TWOPI)

2.2 from…import导入模块中需要的内容

  • from…import可以只导入模块中需要使用的内容

语法格式:

"""
导入格式:     from 模块名 import 需使用的函数、类、变量
使用格式:     函数、类、变量   无需通过模块名引用
"""

示例代码:

from random import randint, Random, TWOPI

# 函数
num = randint(1, 3)
print(num)

# 类
ran = Random()
print(type(ran))

# 变量
print(TWOPI)

2.3 from…import导入模块中所有的内容

语法格式:

"""
导入格式:     from 模块名 import *
使用格式:     函数、类、变量   无需通过模块名引用
"""

示例代码:

from random import *

# 函数
num = randint(1, 3)
print(num)

# 类
ran = Random()
print(type(ran))

2.4 import…as…给导入的模块取别名

  • 把复杂名字改简单些
  • 把已经同名的名字改一个不同名的名字

语法格式:

"""
模块起别名
导入格式:import 模块 as 模块别名
使用格式:模块别名.工具(工具指函数、类、变量)

模块工具起别名
导入格式:from 模块 import 工具 as 工具别名
使用格式:工具别名        无需通过模块名引用
"""

示例代码:

# 将模块random取别名为r
import random as r

# 模块别名.方法
num = r.randint(1, 3)
print(num)

# 模块工具randint取别名为ri
from random import randint as ri

num = ri(1, 3)
print(num)

2.5 模块搜索路径

当你导入一个模块,Python解析器对模块位置的搜索顺序是:

  1. 当前目录
  2. 如果不在当前目录,Python则搜索系统路劲
  3. 模块搜索路径存储在system模块的sys.path变量中。

示例代码:

import sys

# 模块搜索路径存储在system模块的sys.path变量中
print(sys.path)

五、模块制作

1. 定义自己的模块

在Python中,每个Python文件都可以作为一个模块,模块的名字就是文件的名字。比如有这样一个文件module.py,在module.py中定义了所需的函数:

def my_add(a, b):
    """返回2个数相加结果"""
    return a+b


def my_sub(a, b):
    """返回2个数相减结果"""
    return a-b

2. 调用自己定义的模块

import module   # 导入模块

# 调用模块中的函数
ret = module.my_add(1, 1)
print(ret)

ret = module.my_sub(10, 20)
print(ret)

3. 测试模块

3.1 测试模块

在实际开中,当一个开发人员编写完一个模块后,为了让模块能够在项目中达到想要的效果,这个开发人员会自行在模块文件中添加一些测试信息,例如:

module.py:

def my_add(a, b):
    """返回2个数相加结果"""
    return a+b


def my_sub(a, b):
    """返回2个数相减结果"""
    return a-b


ret = my_add(2, 2)
print('模块中测试代码:my_add(2, 2) = ', ret)

ret = my_sub(10, 2)
print('模块中测试代码:my_sub(10, 2) = ', ret)

导入模块文件,默认执行模块文件的内容:

import module 

3.2 模块中的__name__

  • 直接运行此文件,__name__的结果为__main__

  • 此文件被当做模块文件导入时,__name__的结果不为__main__

  • 如果不想导包把模块的测试代码也运行,把模块的测试代码放在if __name__ == '__main__':条件语句里面

4. 模块中的__all__

  • 模块中__all__变量,只对from xxx import *这种导入方式有效

  • 模块中__all__变量包含的元素,才能会被from xxx import *导入

  • __all__格式:

     __all__ = ['变量名', '类名', '函数名', ……]