1 异常
1.1 概述
python程序在编写和运行过程中会产生一些错误,这些错误会导致程序不能按照用户的意图进行工作,甚至由于某些错误的存在,导致程序无法正常运行,或者导致程序终止执行,我们就需要处理这些错误,使得程序能够正常运行。错误在帮助我们尽快修改程序方面起到了非常重要的作用。
程序中常见的错误有语法错误,语义错误,逻辑错误:
- 语法错误: 由于编写程序时没有遵守语法规则,编写了错误的代码,从而导致python解释器无法解释执行源代码而产生的错误。例如,缩进错误,非法字符等等。
- 语义错误:指源程序中不符合语义规则的错误,一条语句视图执行一条不可能的操作而导致的错误。例如,变量定义错误,作用域错误等等。
- 逻辑错误:指程序的运行结果和程序员的预期设想有出入而导致的错误。这类错误并不能直接导致程序运行出现错误,但是未按预期设想的方式运行,产生了不正确的结果。例如:年龄的有效范围为1-100,结果得到的数据超出范围。
简言之,由于程序中的错误,使得程序产生了一些异常行为,我们就需要及时发现并纠正异常行为,从而保证程序的正确运行。
在程序中,异常(exception) 是程序运行过程中发生的事件, 该事件可以中断程序指令的正常执行流程. 一般情况下,当python无法正确处理程序时会抛出异常,如果不处理异常,将会导致程序终止运行。
1.2 异常语法
1.2.1 格式
try:
# 可能会抛出异常的代码段
except 异常类型:
# 处理异常的代码
1.2.2 else用法
在if中,是当条件不满足时执行的实行;同样在try...except...中也是如此,即如果没有捕获到异常,那么就执行else中的事情。
try:
f = open("log.txt","r")
a = 10
b = 1
ret = a / b
#捕获多个类型的异常
except IOError as ex:
print(ex)
except ZeroDivisionError as ex:
print(ex)
else:
print("没有异常,真是happy!")
1.2.3 finally用法
try...finally...语句用来表达这样的情况:在程序中,如果一个段代码无论异常是否产生都要执行,那么此时就需要使用finally
try:
f = open("log.txt","r")
a = 10
b = 0
ret = a / b
#捕获多个类型的异常
except IOError as ex:
print(ex)
except ZeroDivisionError as ex:
print(ex)
else:
print("没有异常,真是happy!")
finally:
print("无论是否异常发生,都会执行!")
1.2.4 try嵌套
import time
try:
f = open('log.txt')
try:
while True:
content = f.readline()
if len(content) == 0:
break
time.sleep(2)
print(content)
finally:
f.close()
print('关闭文件')
except:
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类,通过直接或间接的方式。
# 自定义异常类
class OutOfRangeException(Exception):
def __init__(self,errMsg):
self.msg = errMsg
def __str__(self):
return self.msg
class Person(object):
def __init__(self):
self.name = None
self.age = None
def setAge(self,age):
if age < 0 or age > 100:
raise OutOfRangeException("年龄应该在0-100之间!")
self.age = age
def setName(self,name):
self.name = name
def __str__(self):
return "name:{} age:{}".format(self.name,self.age)
if __name__ == "__main__":
person = Person()
person.setName("Edward")
person.setAge(80)
print(person)
try:
person.setAge(101)
except OutOfRangeException as ex:
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:
# Person类
class Person(object):
def __init__(self,name,age):
self.name = name
self.age = age
def showPerson(self):
print("Name:%s Age:%d"%(self.name,self.age))
# 函数
def myAdd(a,b):
ret = a + b
return ret
other.py
# 引入mymodule模块
import mymodule
# 使用mymodule模块中的myAdd函数
# 注意在使用mymodule中的符号的时候,必须指定模块名.符号的方式
ret = mymodule.myAdd(10,20)
print("ret = %d"%ret)
# 使用mymodule模块中的Person类
person= mymodule.Person("Edward",22)
person.showPerson()
为什么必须加上模块名调用
因为可能存在这样一种情况:在多个模块中含有相同名称的函数,此时如果只是通过函数名来调用,解释器无法知道到底要调用哪个函数。所以如果像上述这样引入模块的时候,调用函数必须加上模块名.
2.1.2 from...import...
有时候我们只需要用到模块中的某个函数,只需要引入该函数即可.通过这种方式引入的时候,调用函数时只能给出函数名,不能给出模块名,但是当两个模块中含有相同名称函数的时候,后面一次引入会覆盖前一次引入。
例如:模块A中有函数function( ),在模块B中也有函数function( ),如果引入A中的function在先、B中的function在后,那么当调用function函数的时候,是去执行模块B中的function函数。如果想一次性引入math中所有的东西,还可以通过from math import *来实现。
# 引入mymodule模块中的myAdd函数
from mymodule import myAdd
# 使用mymodule模块中的myAdd函数
# 注意不可添加模块名
ret = myAdd(10,20)
print("ret = %d"%ret)
# 使用mymodule模块中的Person类出错
# person= mymodule.Person("Edward",22)
# person.showPerson()
# 使用import xxx import *
# 引入mymodule模块中的所有符号
from mymodule import *
# 使用mymodule模块中的myAdd函数
# 注意不可添加模块名
ret = myAdd(10,20)
print("ret = %d"%ret)
# 使用mymodule模块中的Person类出错
person= Person("Edward",22)
person.showPerson()
这提供了简单的方法导入模块中的所有项目, 然而这种方法不应被过多使用。
2.1.3 import...as...
将引入的模块重新命名:
# 引入mymodule并重新命名为md
# 此时再使用mymodule就会未定义
import mymodule as md
# 使用mymodule模块中的myAdd函数
# 注意不可添加模块名
ret = md.myAdd(10,20)
print("ret = %d"%ret)
# 使用mymodule模块中的Person类出错
person= md.Person("Edward",22)
person.showPerson()
2.1.4 定位模块
当导入一个模块,Python对模块位置的搜索顺序是:
- 当前目录;
- PYTHONPATH目录;
- 标准库目录;
- .pth文件目录
模块搜索路径存储在system模块的sys.path变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。
我们可通过配置PYTHONPATH变量、sys.path.append、.pth来添加搜索目录。
2.2 __all__作用
使用from import * 导入某个模块中的符号时,假如我们只想导入部分符号(函数、类、变量),可以通过__all__可以指定那些符号可导出.__all__是一个字符串类型的列表。
__all__ = ["my_function"]
def my_function():
print("my_function!")
def temp_function():
print("temp_function!")
2.3 包
为了更好管理模块,将多个模块放到一个文件夹中,这个文件夹就叫做包,但是此文件夹下必须包含__init__.py文件,该__init__文件可以为空。__init__文件主要用于标识该目录是一个包。
包是一个分层次的文件目录结构,它定义了一个由模块及子包,和子包下的子包组成的Python应用环境。
# 导入package包下mymodule模块
# 方式一
import package.mymodule
package.mymodule.my_function()
# 方式二
# 需要在__init__.py文件
# 添加__all__ = ["mymodule"]
# 指定import *的模块
from package import *
mymodule.my_function()
# 方式三
# 导入模块中指定符号
from package.mymodule import my_function
my_function()
# 引入subpackage包中mymodule2模块
import package.subpackage.mymodule2
package.subpackage.mymodule2.he_function()
from package.subpackage import mymodule2
mymodule2.he_function()
from package.subpackage import *
mymodule2.he_function()
总结:
- 包是一个文件目录
- python包中要包含文件__init__.py
- __init__.py文件中__all__变量可指定from package import *导入的模块