Python学习笔记-异常与IO

感觉已经差不多了,必须要实战下了,当时还是为了爬虫学的,现在发现搞数据挖掘也挺好/火
然而….还是等熟练了再说吧
最近好浮躁啊啊啊!!

错误/异常处理

有一类错误是完全无法在程序运行过程中预测的,比如写入文件的时候,磁盘满了,写不进去了,或者从网络抓取数据,网络突然断掉了。这类错误也称为异常,在程序中通常是必须处理的,否则,程序会因为各种问题终止并退出。
Py使用try...except...finally...的错误处理机制,一个例子:

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

当我们认为某些代码可能会出错时,就可以用try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except语句块,执行完except后,如果有finally语句块,则执行finally语句块,至此,执行完毕。
错误有时不会只有一个,所以可以使用多个except进行处理;所有的错误类型都继承自BaseException这个类
继承关系:https://docs.python.org/3/library/exceptions.html#exception-hierarchy
如果错误没有被捕获,它就会一直往上抛,最后被Python解释器捕获,打印一个错误信息,然后程序退出。
不过报错信息貌似是从下往上看,有点和其他不一样….(调用XX出错,在X行,原因是第X行 :雾)
另外一种形式:

1
2
3
4
5
6
7
8
9
10
try:
pass
except XXXError,e:
pass
except XXXError,e:
pass
else:
pass
finally:
pass

使用try except else 语句,当有异常是会执行except的语句,如果没有异常,则会执行else的语句
或者可以进行简写

1
2
with xxx as f:
pass

xxx其实就是一个类,返回的值存在f中,可以是多个用元组就可以了,比如:open(xx),返回的是file对象,会自动进行关闭操作(无论是否发生了异常),不需要手动关闭
又叫做上下文管理器,执行时调用__enter__方法,返回值存在f;退出时执行__exit__方法(发生异常也执行)

记录错误

Python内置的logging模块可以非常容易地记录错误信息,主要代码:

1
2
3
4
5
6
import logging
try:
bar('0')
except Exception as e:
logging.exception(e)
print('END')

同样是出错,但程序打印完错误信息后会继续执行,并正常退出。通过配置,logging还可以把错误记录到日志文件里,方便事后排查。

抛出异常

raise 主动抛出异常;格式:raise TypeError,"描述",或者抛一个异常对象:

1
2
3
4
5
6
7
8
9
10
11
# 自定义异常
class FooError(ValueError):
pass

def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s) # 异常描述
return 10 / n

foo('0')

raise语句如果不带参数,就会把当前异常原样抛出。此外,在exceptraise一个Error,还可以把一种类型的异常转化成另一种类型

调试

assert 断言语句,一个简单的使用例子:assert 0==1 "描述"会抛出一个assert异常,也就是说,如果后面的判断不成立就会抛出异常
启动Python解释器时可以用-O参数来关闭assert:$ python3 -O err.py;关闭后,你可以把所有的assert语句当成pass来看。

使用logging

和assert比,logging不会抛出错误,而且可以输出到文件;
它允许你指定记录信息的级别,有debug,info,warning,error等几个级别,当我们指定level=INFO时,logging.debug就不起作用了。同理,指定level=WARNING后,debug和info就不起作用了。
是不是和某Log比较相似…

1
2
import logging
logging.basicConfig(level=logging.INFO)

logging的另一个好处是通过简单的配置,一条语句可以同时输出到不同的地方,比如console和文件。

pdb单步调试

其实就是单步调试,最好还是使用IDE
手动的话就是:$ python3 -m pdb err.py
输入命令l来查看代码;
输入命令n可以单步执行代码;
任何时候都可以输入命令p 变量名来查看变量;
输入命令q结束调试,退出程序

文件读写

读写文件是最常见的IO操作。Python内置了读写文件的函数,用法和C是兼容的。

读写文件前,我们先必须了解一下,在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘,所以,读写文件就是请求操作系统打开一个文件对象通常称为文件描述符),然后,通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件)。

读文件

打开一个文件对象就是open("name","role"),关于权限,常用的有这几种:

权限描述
r只读,文件必须存在,否则会抛异常
w只写,文件不存在时创建文件,存在则清空文件内容
a追加,文件不存在时创建文件
r+打开文件会保持原文件内容不变,同样可以同时对文件进行读写,覆盖追加
w+打开文件会将原文件内容删除,可以同时对文件进行读写
a+追加和读写方式
rb,wb,ab,rb+,wb+,ab+二进制方式打开(读取图片等)

拿到文件对象后就可以进行读取内容了,可以使用read([size])读取内容,也可以使用readline([size]) 读取一行;使用readlines([size]) 读取完缓冲区左右(io.DEFAULT_BUFFER_SIZE),返回每一行组成的列表.
最后一步是调用close()方法关闭文件。文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的,但是有可能没有关闭之前出现了异常,所以最好放在try ... finally
可以直接使用for in来读取file对象的每行的数据

file-like Object

open()函数返回的这种有个read()方法的对象,在Python中统称为file-like Object。除了file外,还可以是内存的字节流,网络流,自定义流等等。file-like Object不要求从特定类继承,只要写个read()方法就行。

StringIO就是在内存中创建的file-like Object,常用作临时缓冲。

字符编码

要读取非UTF-8编码的文本文件,需要给open()函数传入encoding参数,例如:open('./gbk.txt', 'r', encoding='gbk');如果遇到非法编码的字符,open()函数还接收一个errors参数,表示如果遇到编码错误后如何处理。最简单的方式是直接忽略:open('./gbk.txt', 'r', encoding='gbk', errors='ignore')

写入到文件

还是用的open这个函数,但是权限不同了,上面的表已经写的很明白了
你可以反复调用write()来写入文件,但是务必要调用f.close()来关闭文件。当我们写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。只有调用close()方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用close()的后果是数据可能只写了一部分到磁盘,剩下的丢失了。当然也可以使用flush进行刷新;所以,还是用with语句来得保险:

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

要写入特定编码的文本文件,请给open()函数传入encoding参数,将字符串自动转换成指定编码。

使用with语句操作文件IO是个好习惯。

文件指针

seek()函数
接受2个参数:1.偏移量 2.相对偏移位置

file.tell() 返回文件的偏移
os.SEEK_SET:相对文件起始位置
os.SEEK_CUR:当前位置
os.SEEK_END:末尾位置

例子:
f.seek(0,os.SEEK_SET):使文件指针回到开始位置;
f.seek(-5,os.SEEK_CUR):从后向前移动5个字节
f.seek(0,os.SEEK_END):将文件指针移到最后

文件属性

file.fileno() 文件描述符
.mode 文件打开权限
.closed 是否正确关闭
.enconding 编码方式

文件标准输入:sys.stdin;
raw_input()该函数是从命令行接受输入,回车终止,可以传一个提示参数str
文件标准输出:sys.stdout;
文件标准错误:sys.stderr;

sys模块提供sys.argv属性,通过该属性可以得到命令行参数;
sys.argv:字符串组成的列表,第一个参数多为文件名
codecs模块支持open指定编码

StringIO和BytesIO

很多时候,数据读写不一定是文件,也可以在内存中读写。

StringIO

StringIO顾名思义就是在内存中读写str。下面是个例子:

1
2
3
4
5
6
7
8
9
10
>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5
>>> f.write(' ')
1
>>> f.write('world!')
6
>>> print(f.getvalue())
hello world!

getvalue()方法用于获得写入后的str。要读取StringIO,可以用一个str初始化StringIO,然后,像读文件一样读取:

1
2
3
4
5
6
7
8
9
10
11
>>> from io import StringIO
>>> f = StringIO('Hello!\nHi!\nGoodbye!')
>>> while True:
... s = f.readline()
... if s == '':
... break
... print(s.strip())
...
Hello!
Hi!
Goodbye!

BytesIO

StringIO操作的只能是str,如果要操作二进制数据,就需要使用BytesIO。

1
2
3
4
5
6
>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('中文'.encode('utf-8'))
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'

请注意,写入的不是str,而是经过UTF-8编码的bytes。和StringIO类似,可以用一个bytes初始化BytesIO,然后,像读文件一样读取.

OS模块

我们也是可以使用OS模块打开文件的(更贴近底层以及为了跨平台),有更多的方法,先把常用的方法列一下

os.open(filename, flag [,mode]):打开文件
flag打开方式:
os.O_CREAT:创建文件
os.O_RDONLY:只读方式打开
os.O_WRONLY:只写方式打开
os.O_RDWR:读写方式打开

os.read(fd, buffersize):读取文件
os.write(fd, string):写入文件
os.lseek(fd, pos, how): 文件指针操作
os.close(fd):关闭文件

os.access(path, mode): F_OK, R_OK ,W_OK, X_OK 判断是否有权限
os.listdir(path): 返回当path路径下所有文件名组成的列表
os.remove(path):删除文件
os.rename(old, new):修改文件或者目录名
os.mkdir(path[, mode]):创建目录
os.makedirs(path[, mode]):创建多级目录
os.removedirs(path):删除多级目录
os.rmdir(path):删除目录(目录必须空目录)

os.path.exists(path):当前路径是否存在 | 也可以判断是否有该文件
os.path.isdir(s):是否是一个目录
os.path.isfile(path):是否是一个文件
os.path.getsize(filename):返回文件大小 | 返回目录文件大小
os.path.dirname(p):返回路径的目录
os.path.basename(p):返回路径的文件名
os.path.split():拆分出前面的路径和最好一级目录或文件
os.path.splitext():拆分文件后缀名

os.name():系统类型 posix/nt
os.uname():查看系统信息
os.environ:环境变量(可以指定key比如:os.environ.get(‘PATH’))

序列化

Python提供了pickle模块来实现序列化。使用前注意Py的版本

1
2
3
4
5
6
7
8
>>> import pickle
>>> d = dict(name='Bob', age=20, score=88)
>>> pickle.dumps(d)
b'\x80\x03}q\x00(X\x03\x00\x00\x00ageq\x01K\x14X\x05\x00\x00\x00scoreq\x02KXX\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00Bobq\x04u.'

>>> f = open('dump.txt', 'wb')
>>> pickle.dump(d, f)
>>> f.close()

pickle.dumps()方法把任意对象序列化成一个bytes,然后,就可以把这个bytes写入文件。或者用另一个方法pickle.dump()直接把对象序列化后写入一个file-like Object.
当我们要把对象从磁盘读到内存时,可以先把内容读到一个bytes,然后用pickle.loads()方法反序列化出对象,也可以直接用pickle.load()方法从一个file-like Object中直接反序列化出对象。

为了方便在不同语言间传递,我们一般序列化为XML或者JSON格式,JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。

JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下:

JSON类型Python类型
{}dict
[]list
“string”str
1234.56int或float
true/falseTrue/False
nullNone

Python内置的json模块提供了非常完善的Python对象到JSON格式的转换。

参考

关于with详细解释
http://www.liaoxuefeng.com

喜欢就请我吃包辣条吧!

评论框加载失败,无法访问 Disqus

你可能需要魔法上网~~