您的位置:首页 > 编程语言 > Python开发

Python学习笔记(五)异常处理与文件

2019-05-26 15:57 447 查看

1.错误处理:

当我们认为某些代码可能会出错时,就可以用

try
来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即
except
语句块,执行完
except
后,如果有
finally
语句块,则执行
finally
语句块,至此,执行完毕。

[code]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"""

Python的错误其实也是class,所有的错误类型都继承自

BaseException
,所以在使用
except
时需要注意的是,它不但捕获该类型的错误,还把其子类也“一网打尽”。比如

[code]try:
foo()
except ValueError as e:
print('ValueError')
except UnicodeError as e:
print('UnicodeError')

第二个

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

使用

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

2.调试

i.第一种方法简单直接粗暴有效,就是用

print()
把可能有问题的变量打印出来看看。

ii.断言:

assert
的意思是,表达式
n != 0
应该是
True
,否则,根据程序运行的逻辑,后面的代码肯定会出错。如果断言失败,
assert
语句本身就会抛出
AssertionError

[code]def foo(s):
n = int(s)
assert n != 0, 'n is zero!'
return 10 / n

def main():
foo('0')
"""输出如下
$ python err.py
Traceback (most recent call last):
...
AssertionError: n is zero!
"""

iii.把

print()
替换为
logging
是第3种方式,和
assert
比,
logging
不会抛出错误,而且可以输出到文件:

[code]import logging
logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)
"""输出如下
INFO:root:n = 0
Traceback (most recent call last):
File "err.py", line 8, in <module>
print(10 / n)
ZeroDivisionError: division by zero
"""

这就是

logging
的好处,它允许你指定记录信息的级别,有
debug
info
warning
error
等几个级别,当我们指定
level=INFO
时,
logging.debug
就不起作用了。同理,指定
level=WARNING
后,
debug
info
就不起作用了。这样一来,你可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。

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

iiii.第4种方式是启动Python的调试器pdb,让程序以单步方式运行,可以随时查看运行状态。

iiiii.pycharm等IDE

3.测试:单元测试与文档测试

4.IO编程:

IO在计算机中指Input/Output,也就是输入和输出。由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘、网络等,就需要IO接口。

由于CPU和内存的速度远远高于外设的速度,所以,在IO编程中,就存在速度严重不匹配的问题。举个例子来说,比如要把100M的数据写入磁盘,CPU输出100M的数据只需要0.01秒,可是磁盘要接收这100M数据可能需要10秒,怎么办呢?有两种办法:

第一种是CPU等着,也就是程序暂停执行后续代码,等100M的数据在10秒后写入磁盘,再接着往下执行,这种模式称为同步IO

另一种方法是CPU不等待,只是告诉磁盘,“您老慢慢写,不着急,我接着干别的事去了”,于是,后续代码可以立刻接着执行,这种模式称为异步IO

同步和异步的区别就在于是否等待IO执行的结果。

5.读写文件:
读文件:

[code]f=open('tips.txt','r') # 相对路径
print(f.read())
f.close()
fs=open('D:/临时文件/test.txt','r') # 绝对路径
print(fs.read())
fs.close()

使用python内置的open()函数以读文件模式打开文件,如果文件不存在,

open()
函数就会抛出一个
IOError
的错误。如果文件打开成功,接下来,调用
read()
方法可以一次读取文件的全部内容,Python把内容读到内存,用一个
str
对象表示。最后一步是调用
close()
方法关闭文件。文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的。

由于文件读写时都有可能产生

IOError
,一旦出错,后面的
f.close()
就不会调用。所以,为了保证无论是否出错都能正确地关闭文件,我们可以使用
try ... finally
来实现:

[code]try:
f = open('/path/to/file', 'r')
print(f.read())
finally:
if f:
f.close()

但是每次都这么写实在太繁琐,所以,Python引入了

with
语句来自动帮我们调用
close()
方法:

[code]with open('/path/to/file', 'r') as f:
print(f.read())

调用

read()
会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用
read(size)
方法,每次最多读取size个字节的内容。另外,调用
readline()
可以每次读取一行内容,调用
readlines()
一次读取所有内容并按行返回
list
。因此,要根据需要决定怎么调用。

如果文件很小,

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

[code]for line in f.readlines():
print(line.strip()) # 把末尾的'\n'删掉

以下分别是逐行读取文件内容和逐行将文件内容保存到列表:

[code]with open('tips.txt','r') as f:
for line in f:
print(line.rstrip())
with open('tips.txt') as f:
lines = f.readlines()
print(lines)

字符编码:要读取非UTF-8编码的文本文件,需要给

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

[code]f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')

打开模式:

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

注:后面有带b的方式,不需要考虑编码方式。有带+号的,则可读可写,不过它们之间还是有区别的

文件路径:有相对和绝对两种,注意采用 ' \ ' 可能会形成转义字符,建议使用' / '

写文件:

[code]f = open('tests.txt', 'w')
f.write('Hello, world!')
f.close()

写文件和读文件是一样的,唯一区别是调用

open()
函数时,传入标识符
'w'
或者
'wb'
表示写文本文件或写二进制文件。以
'w'
模式写入文件时,如果文件已存在,会直接覆盖(相当于删掉后新写入一个文件)。如果我们希望追加到文件末尾怎么办?可以传入
'a'
以追加(append)模式写入。可以反复调用
write()
来写入文件,但是务必要调用
f.close()
来关闭文件。当我们写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。只有调用
close()
方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用
close()
的后果是数据可能只写了一部分到磁盘,剩下的丢失了。所以,还是用
with
语句来得保险。

6.StringIO和BytesIO:

很多时候,数据读写不一定是文件,也可以在内存中读写。StringIO顾名思义就是在内存中读写str。要把str写入StringIO,我们需要先创建一个StringIO,然后,像文件一样写入即可:

[code]>>> 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,然后,像读文件一样读取:

[code]>>> from io import StringIO
>>> f = StringIO('Hello!\nHi!\nGoodbye!')
>>> while True:
...     s = f.readline()
...     if s == '':
...         break
...     print(s.strip())
...
Hello!
Hi!
Goodbye!

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

StringIO和BytesIO是在内存中操作str和bytes的方法,使得和读写文件具有一致的接口。

7.操作文件和目录

相关函数一部分放在os模块中,一部分放在os.path模块中!查看,创建,删除目录方法如下:

[code]import os
import os.path
print(os.path.abspath('.')) # 查看当前目录的绝对路径
print(os.path.join('D:/', 'testdir')) # 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来:
os.mkdir('D:/testdir') # 然后创建一个目录
os.rmdir('D:/testdir') # 删掉一个目录
print(os.path.splitext('D:/testdir')) # 得到文件扩展名

把两个路径合成一个时,不要直接拼字符串,而要通过

os.path.join()
函数,这样可以正确处理不同操作系统的路径分隔符。同样的道理,要拆分路径时,也不要直接去拆字符串,而要通过
os.path.split()
函数,这样可以把一个路径拆分为两部分,后一部分总是最后级别的目录或文件名。
os.path.splitext()
可以直接让你得到文件扩展名,很多时候非常方便。

文件操作使用下面的函数。假定当前目录下有一个

test.txt
文件:

[code]os.rename('test.txt', 'test.py') # 对文件重命名
os.remove('test.py') # 删掉文件

shutil
模块提供了
copyfile()
的函数,你还可以在
shutil
模块中找到很多实用函数,它们可以看做是
os<
20000
/code>模块的补充。

要列出当前目录下的所有目录,只需要一行代码:

[code] print([x for x in os.listdir('.') if os.path.isdir(x)])

要列出所有的

.py
文件,也只需一行代码:

[code]print([x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py'])

8.序列化:主要用到pickle.dumps(),json.dumps()两种函数,详情略。

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: