Python 基础五

捕捉异常、模块、包、文件基础错操作

知识点

1、捕捉异常点

一旦出错,还要一级一级上报,知道查到某个函数可以处理该错误(比如,给用户输出一个错误信息)。所以高级语言通常可以内置一套 try..expect..finally. .的错误处理机制,Python也不例外。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
try:
print('try...')
r = 10 / 0
print('result:',r)
except ZeroDivisionError as e:
print('except:',e)
finally:
print('finally...')
print('END')
-------------------------
try...
except: division by zero
finally...
END

当我们认为某些代码可能会出错时,就可以用 try 来运行这段代码,如果执行出错,则后续代码不会继续执行 ,而是直接跳转至错误处理代码,及 except 语句块,执行完 except 后,如果有 finally 语句块,则执行finally 语句块,至此,执行完毕。

上面的代码在计算 10/0 时会产生一个除法运算错误:

1
2
3
4
try...
except: division by zero
finally...
END

从输出可以看出,当发生错误时,后续语句 print('result:',r) 不会被执行,except 由于捕捉到 ZeroDivisionError ,因此被执行。最后,fianlly 语句被执行。

如果把 0 改成 2 ,则执行结果如下:

1
2
3
4
try...
result: 5.0
finally...
END

由于没有错误发生,所以 except 语句块不会被执行,但是 finally 如果有,则一定会被执行(可以没有finally 语句)。

错误应该有多种种类,如果发生了不同类型的错误,应该由不同的 except 语句块处理。

1
2
3
4
5
6
7
8
9
10
11
try:
print('try...')
r = 10 / int('a')
print('result:',r)
except ValueError as e:
print('ValueError:',e)
except ZeroDivisionError as e:
print('except:',e)
finally:
print('finally...')
print('END')

int() 函数可能会抛出 ValueError,所以可以用一个except 来捕捉 ValueError ,用另一个 except 捕捉 ZeroDivisionError

此外,如果没有错误发生,可以在 except 后面加一个 else ,当没有错误发生时,会自动执行 else 语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
try:
print('try...')
r = 10 / int('a')
print('result:',r)
except ValueError as e:
print('ValueError:',e)
except ZeroDivisionError as e:
print('except:',e)
else:
print('no error!')
finally:
print('finally...')
print('END')

Python 的错误其实也是 class ,所有的错误类型都 继承BaseException ,所以在使用 except 时需要注意的是,它不但捕获该类型的错误,还把其子类都一网打尽,比如:

1
2
3
4
5
6
try:
foo()
except ValueError as e:
print('ValueError')
except UnicodeError as e:
print('UnicodeError')

第二个 except 永远也捕获不到 UnicodeError ,因为 UnicodeErrorValueError 的子类,如果有,也是被第一个 except 捕获了。

Python 所有错误都从 BaseException 类派生的,常见的错误类型和继承关系:

https://docs.python.org/3/library/exceptions.html#exception-hierarchy

使用 try...except 捕获错误还有一个好处,就是可以跨越多层调用,比如函数 main() 调用 foo()foo() 调用 bar() , 结果 bar() 出错了,这时,只要 main() 捕获到了,就可以处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
def foo(s):
return 10/int(s)

def bars(s):
return foo(s) * 2

def main():
try:
bar('0')
except Exception as e:
print('Errot:',e)
finally:
print('finally...')

也就是说不需要在每一个可能出错的地方去捕获错误,只要在合适的层次无捕获就可以了,这样可以大大减少写 try...except...finally 的麻烦。

python 所有的标准异常类:

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

2. 模块

模块: 通俗理解一个 .py 文件就是一个模块,模块是管理功能代码的。

内置模块: 就是 Python 自己内部自带的模块,如 time、random 等。

2.1 自定义模块的使用

注意:自定义模块名字和变量名定义很类似,都是由字母、数字、下划线组成,但是不能以数字开头。

创建名为 first_model 的自定义模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__all__ = ['f_num','show']
# 指定 __all__表示 from 模块名 import * 只能使用指定的功能代码,而不是所有的功能代码

# 定义全局变量
g_num = 10

# 定义函数
def show():
print('我是一个函数')
# 定义类
class Student(object):
def __init__(self,name,age):
self.name = name
self.sge = age
def show_msg(self):
print(self.name,self.age)
# 解决导入的模块中方法没有调用就会执行
if __name__ == '__main__':
show()

使用自定义的模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 导入模块
# 1. import 直接导入
import first_model
# 使用模块中的功能代码
print(first_model.g_num)
first_model.show()
stu = first_model.Student('李四',20)
stu.show_msg()

# 2. 从first——model 这个模块导入 show 的方法
from first_model import show

# 给模块起别名(避免多个模块中有一样的名字)
from second_model import show as second_show

# 导入一个模块的多个方法
from first_model import show,Student

# 导入一个模块的多个方法
from first_model import *

注意: 使用 __name__ 查看模块名,执行哪个文件,哪个文件中的 __name__输出__main__ ,其他导入的 __name__ 结果就是模块名字。

模块导入注意点:
1. 自定义的模块名不要和系统的模块名重名,
2. 导入的功能代码不要再当前模块定义否则使用不了导入模块的功能代码

3. 包的介绍

包: 通俗理解包就是一个文件夹,只不过文件夹里由一个 init.py 文件,包是管理模块的,模块是管理功能代码的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# -----import 导入包里的模块-----
import first_package.first

# -----import 导入包里的模块设置别名-----
import first_package.first as one

# -----from 导入包名 import 模块名-----
from first_package import second

# -----from 包名.模块名 import 功能代码-----
from first_package.first import show

# =====form 包名 import* ,默认不会导入包里面的所有模块,需要在init 文件里面使用 __all__ 去指定导入的模块
from first_packageckage import *

__init__ 文件写法

1
2
3
4
# 如果外界使用 from 包名 import *  ,默认不会导入包里面的所有模块,需要使用 __all__指定 __all__ =['first','second']
# 从当前包导入对应模块
from . import first
from . import second

4. 文件的基本操作

读文件

要以读文件的模式打开一个文件对象,要使用Python 内置的 open() 函数,传入文件名和标识符。

1
>>> f = open('/Users/python/test.txt','r')

标识符 ‘r’ 表示读, 这样,就成功地打开了一个文件。

如果文件打开成功,可以用read() 方法读取全部内容。Python 把内容读到内存,用一个str 对象表示

1
2
>>> f.read()
'Hello,world'

最后一步 调用close() 方法关闭文件。文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的。

1
>>> f.close()

由于文件读写时可能产生 IOError ,一旦出错,后面的 f.close() 也就不会调用,所以,为了保证无论是否出错都能正确关闭文件,可以使用 try...finally 来实现

1
2
3
4
5
6
try:
f = open('/Users/python/test.txt','r')
print(f.read())
finally:
if f:
f.close()

但是每次这样写太麻烦,所以 Python 引入 with 语句来自动帮我我们调用 close()

1
2
with open('/Users/python/test.txt','r') as f:
print(f.read())

调用 read() 会一次性读取文件的全部内容,如果文件很大,内存就爆了。保险起见,可以反复调用 read(size) 方法,每次最多读取size 个字节的内容。另外,readline() 可以每次读取一行内容,调用readlines() 一次读取所有内容并按行返回 list

如果文件很小,read() 一次读取最方便;如果不能确定文件大小,反复调用read(size) 比较保险;如果是配置文件,调用 readlines() 最方便。

1
2
for line in f.readlines():
print(line.strip()) # 把末尾的'/n' 删掉

文件的打开方式

模式说明
r以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
w打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
a打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
rb以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。
wb以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
ab以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
r+打开一个文件用于读写。文件指针将会放在文件的开头.
w+打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
a+打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
rb+以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
wb+以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
ab+以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

字符编码

要读取非 UTF-8 编码的文本文件,需要给 open() 函数传入 encoding 参数,例如,读取GBK编码的文件

1
2
>>> f= open('/Users/python/test.txt','r',encoding='gbk')
>>> f.read()

遇到编码不规范的文件,可能会遇到 UnicodeDecodeError,因为文本文件中可能夹杂了一些非法变法的字符。遇到这种情况,open() 函数还接受一个 errors 参数,表示如果遇到编码错误后如何处理。最简单的方式是直接忽略。

1
>>> f = open('/Users/python/test.txt','r',encoding='gbk',errors='ignore')

写文件

写文件和读文件一样,唯一区别是调用 open() 函数时,传入 表示符 'w' 或者 'wb' 表示写文本文件或者二进制文件:

1
2
3
>>> f = open('/Users/python/test.txt','w') 
>>> f.write('Hello,Woeld!')
>>> f.close()

可以反复使用 write() 来写入文件,但是必须要使用 f.close() 来关闭文件。当我们写如文件时,操作系统往往不会立马把数据写入磁盘,而是放到缓存里空闲时间再慢慢写入。只有调用 close() 方法时,操作系统才能保证包所有数据写入磁盘。所以还是可以利用 with 语句。

1
2
with open('/Users/python/test.txt','w') as f:
f.write('Hello,world!')

要写入特定编码文本文件,要给 open() 函数传入 encoding 参数,将字符串转换为指定编码

注意:'w' 模式写入文件时,如果文件已经存在,会直接覆盖(相当于删掉后新写入一个文件)。可以传入 'a' 来追加 (append)模式写入。

感谢您的阅读,本文由 LEE 版权所有。如若转载,请注明出处:LEE(https://ChubbyLEE-Math.github.io/2020/07/08/python%20%E5%9F%BA%E7%A1%80%E4%BA%94/
Python 基础四
Hello World