• python基础知识整理 06-异常、模块和包


    1 异常

    1.1 概述

    python程序在编写和运行过程中会产生一些错误,这些错误会导致程序不能按照用户的意图进行工作,甚至由于某些错误的存在,导致程序无法正常运行,或者导致程序终止执行,我们就需要处理这些错误,使得程序能够正常运行。错误在帮助我们尽快修改程序方面起到了非常重要的作用。

    程序中常见的错误有语法错误,语义错误,逻辑错误:

    • 语法错误: 由于编写程序时没有遵守语法规则,编写了错误的代码,从而导致python解释器无法解释执行源代码而产生的错误。例如,缩进错误,非法字符等等。
    • 语义错误:指源程序中不符合语义规则的错误,一条语句视图执行一条不可能的操作而导致的错误。例如,变量定义错误,作用域错误等等。
    • 逻辑错误:指程序的运行结果和程序员的预期设想有出入而导致的错误。这类错误并不能直接导致程序运行出现错误,但是未按预期设想的方式运行,产生了不正确的结果。例如:年龄的有效范围为1-100,结果得到的数据超出范围。

    简言之,由于程序中的错误,使得程序产生了一些异常行为,我们就需要及时发现并纠正异常行为,从而保证程序的正确运行。

    在程序中,异常(exception) 是程序运行过程中发生的事件, 该事件可以中断程序指令的正常执行流程. 一般情况下,当python无法正确处理程序时会抛出异常,如果不处理异常,将会导致程序终止运行。

    1.2 异常语法

    1.2.1 格式

    1. try
    2. # 可能会抛出异常的代码段
    3. except 异常类型:
    4. # 处理异常的代码

    1.2.2 else用法

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

    1. try:
    2. f = open("log.txt","r")
    3. a = 10
    4. b = 1
    5. ret = a / b
    6. #捕获多个类型的异常
    7. except IOError as ex:
    8. print(ex)
    9. except ZeroDivisionError as ex:
    10. print(ex)
    11. else:
    12. print("没有异常,真是happy!")

    1.2.3 finally用法

    try...finally...语句用来表达这样的情况:在程序中,如果一个段代码无论异常是否产生都要执行,那么此时就需要使用finally

    1. try:
    2. f = open("log.txt","r")
    3. a = 10
    4. b = 0
    5. ret = a / b
    6. #捕获多个类型的异常
    7. except IOError as ex:
    8. print(ex)
    9. except ZeroDivisionError as ex:
    10. print(ex)
    11. else:
    12. print("没有异常,真是happy!")
    13. finally:
    14. print("无论是否异常发生,都会执行!")

    1.2.4 try嵌套

    1. import time
    2. try:
    3. f = open('log.txt')
    4. try:
    5. while True:
    6. content = f.readline()
    7. if len(content) == 0:
    8. break
    9. time.sleep(2)
    10. print(content)
    11. finally:
    12. f.close()
    13. print('关闭文件')
    14. except:
    15. print("没有这个文件")

    1.2.5 自定义异常

    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

    内存溢出错误(对于Py解释器不是致命的)

    NameError

    未声明/初始化对象 (没有属性)

    UnboundLocalError

    访问未初始化的本地变量

    ReferenceError

    弱引用试图访问已经垃圾回收了的对象

    RuntimeError

    一般的运行时错误

    NotImplementedError

    尚未实现的方法

    SyntaxError

    Python 语法错误

    IndentationError

    缩进错误

    TabError

    Tab 和空格混用

    SystemError

    一般的解释器系统错误

    TypeError

    对类型无效的操作

    ValueError

    传入无效的参数

    UnicodeError

    Unicode 相关的错误

    UnicodeDecodeError

    Unicode 解码时的错误

    UnicodeEncodeError

    Unicode 编码时错误

    UnicodeTranslateError

    Unicode 转换时错误

    Warning

    警告的基类

    DeprecationWarning

    关于被弃用的特征的警告

    FutureWarning

    关于构造将来语义会有改变的警告

    OverflowWarning

    旧的关于自动提升为长整型(long)的警告

    PendingDeprecationWarning

    关于特性将会被废弃的警告

    可以用raise语句来引发一个异常。通过创建一个新的异常类,程序可以命名它们自己的异常。异常应该是典型的继承自Exception类,通过直接或间接的方式。

    1. # 自定义异常类
    2. class OutOfRangeException(Exception):
    3. def __init__(self,errMsg):
    4. self.msg = errMsg
    5. def __str__(self):
    6. return self.msg
    7. class Person(object):
    8. def __init__(self):
    9. self.name = None
    10. self.age = None
    11. def setAge(self,age):
    12. if age < 0 or age > 100:
    13. raise OutOfRangeException("年龄应该在0-100之间!")
    14. self.age = age
    15. def setName(self,name):
    16. self.name = name
    17. def __str__(self):
    18. return "name:{} age:{}".format(self.name,self.age)
    19. if __name__ == "__main__":
    20. person = Person()
    21. person.setName("Edward")
    22. person.setAge(80)
    23. print(person)
    24. try:
    25. person.setAge(101)
    26. except OutOfRangeException as ex:
    27. print(ex)

    1.3 总结

    如果try嵌套,那么如果里面的try没有捕获到这个异常,那么外面的try会接收到这个异常,然后进行处理,如果外边的try依然没有捕获到,那么再进行传递。

    如果一个异常是在一个函数中产生的,例如函数A---->函数B---->函数C,而异常是在函数C中产生的,那么如果函数C中没有对这个异常进行处理,那么这个异常会传递到函数B中,如果函数B有异常处理那么就会按照函数B的处理方式进行执行;如果函数B也没有异常处理,那么这个异常会继续传递,以此类推。。。如果所有的函数都没有处理,那么此时就会进行异常的默认处理,即通常见到的那样。

    当调用test3函数时,在test1函数内部产生了异常,此异常被传递到test3函数中完成了异常处理,而当异常处理完后,并没有返回到函数test1中进行执行,而是在函数test3中继续执行。

    2 模块和包

    2.1 使用模块

    2.1.1 import

    比如,编写了一个模块mymodule.py,在该模块中我们定义一些函数和类,在other.py使用

    mymodule.py:

    1. # Person类
    2. class Person(object):
    3. def __init__(self,name,age):
    4. self.name = name
    5. self.age = age
    6. def showPerson(self):
    7. print("Name:%s Age:%d"%(self.name,self.age))
    8. # 函数
    9. def myAdd(a,b):
    10. ret = a + b
    11. return ret

    other.py

    1. # 引入mymodule模块
    2. import mymodule
    3. # 使用mymodule模块中的myAdd函数
    4. # 注意在使用mymodule中的符号的时候,必须指定模块名.符号的方式
    5. ret = mymodule.myAdd(10,20)
    6. print("ret = %d"%ret)
    7. # 使用mymodule模块中的Person类
    8. person= mymodule.Person("Edward",22)
    9. person.showPerson()

    为什么必须加上模块名调用

    因为可能存在这样一种情况:在多个模块中含有相同名称的函数,此时如果只是通过函数名来调用,解释器无法知道到底要调用哪个函数。所以如果像上述这样引入模块的时候,调用函数必须加上模块名.

    2.1.2 from...import...

    有时候我们只需要用到模块中的某个函数,只需要引入该函数即可.通过这种方式引入的时候,调用函数时只能给出函数名,不能给出模块名,但是当两个模块中含有相同名称函数的时候,后面一次引入会覆盖前一次引入。

    例如:模块A中有函数function( ),在模块B中也有函数function( ),如果引入A中的function在先、B中的function在后,那么当调用function函数的时候,是去执行模块B中的function函数。如果想一次性引入math中所有的东西,还可以通过from math import *来实现。

    1. # 引入mymodule模块中的myAdd函数
    2. from mymodule import myAdd
    3. # 使用mymodule模块中的myAdd函数
    4. # 注意不可添加模块名
    5. ret = myAdd(10,20)
    6. print("ret = %d"%ret)
    7. # 使用mymodule模块中的Person类出错
    8. # person= mymodule.Person("Edward",22)
    9. # person.showPerson()
    10. # 使用import xxx import *
    11. # 引入mymodule模块中的所有符号
    12. from mymodule import *
    13. # 使用mymodule模块中的myAdd函数
    14. # 注意不可添加模块名
    15. ret = myAdd(10,20)
    16. print("ret = %d"%ret)
    17. # 使用mymodule模块中的Person类出错
    18. person= Person("Edward",22)
    19. person.showPerson()

    这提供了简单的方法导入模块中的所有项目, 然而这种方法不应被过多使用。

    2.1.3 import...as...

    将引入的模块重新命名:

    1. # 引入mymodule并重新命名为md
    2. # 此时再使用mymodule就会未定义
    3. import mymodule as md
    4. # 使用mymodule模块中的myAdd函数
    5. # 注意不可添加模块名
    6. ret = md.myAdd(10,20)
    7. print("ret = %d"%ret)
    8. # 使用mymodule模块中的Person类出错
    9. person= md.Person("Edward",22)
    10. person.showPerson()

    2.1.4 定位模块

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

    • 当前目录;
    • PYTHONPATH目录;
    • 标准库目录;
    • .pth文件目录

    模块搜索路径存储在system模块的sys.path变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。

    我们可通过配置PYTHONPATH变量、sys.path.append、.pth来添加搜索目录。

    2.2 __all__作用

    使用from import * 导入某个模块中的符号时,假如我们只想导入部分符号(函数、类、变量),可以通过__all__可以指定那些符号可导出.__all__是一个字符串类型的列表。

    1. __all__ = ["my_function"]
    2. def my_function():
    3. print("my_function!")
    4. def temp_function():
    5. print("temp_function!")

    2.3 包

    为了更好管理模块,将多个模块放到一个文件夹中,这个文件夹就叫做包,但是此文件夹下必须包含__init__.py文件,该__init__文件可以为空。__init__文件主要用于标识该目录是一个包。

    包是一个分层次的文件目录结构,它定义了一个由模块及子包,和子包下的子包组成的Python应用环境。

    1. # 导入package包下mymodule模块
    2. # 方式一
    3. import package.mymodule
    4. package.mymodule.my_function()
    5. # 方式二
    6. # 需要在__init__.py文件
    7. # 添加__all__ = ["mymodule"]
    8. # 指定import *的模块
    9. from package import *
    10. mymodule.my_function()
    11. # 方式三
    12. # 导入模块中指定符号
    13. from package.mymodule import my_function
    14. my_function()
    15. # 引入subpackage包中mymodule2模块
    16. import package.subpackage.mymodule2
    17. package.subpackage.mymodule2.he_function()
    18. from package.subpackage import mymodule2
    19. mymodule2.he_function()
    20. from package.subpackage import *
    21. mymodule2.he_function()

    总结:

    • 包是一个文件目录
    • python包中要包含文件__init__.py
    • __init__.py文件中__all__变量可指定from package import *导入的模块
  • 相关阅读:
    基于Java毕业设计智能导诊系统源码+系统+mysql+lw文档+部署软件
    rules使用正则表达式进行校验手机号
    经纬度坐标为中心点生成米距离长度半径的圆形面,含java js源码+在线绘制,代码简单零依赖
    vue2添加(修改)数据后需要刷新才能显示的问题
    获取外网IP接口
    简析CloudCompare文件夹之间的关系
    鸿鹄工程项目管理系统 Spring Cloud+Spring Boot+Mybatis+Vue+ElementUI+前后端分离构建工程项目管理系统项目背景
    javascript正则表达式(语法以及正则表达式修饰符)
    实现两数交换
    RPC协议详解
  • 原文地址:https://blog.csdn.net/bnever/article/details/125540439