之前的文章介绍的都是Python
的一些语法和使用方法, 详情可参考Python知识
然而这里我们要说的是编程中我们最不想见到的, 但是却也是不得不面对的Bug
除此之外, 这里还会介绍一下Python
中的文件读取
错误和异常 Python
中(至少)有两种错误:语法错误和异常(syntax errors
和 exceptions
)
语法错误 语法错误,也被称作解析错误, 使我们在学习Python
过程中最常遇到的错误, 来看看下面两个错误示例:
1 2 3 4 5 6 7 8 if True print ('titan' ) File "../5-读文件.py" , line 19 if True ^ SyntaxError: invalid syntax
语法分析器指出错误出现的文件(File
)和错误行(line 19
)
在检测到错误的位置前面显示一个小“箭头”
错误是由箭头前面的标记引起的(或者至少是这么检测的)
此处错误是因为Print
函数的前面, if
语句后面少了一个冒号(:
)
异常
在没有语法错误的情况下, 当我们执行当前程序的时候也可能会引发错误
运行期检测到的错误称为 异常,并且程序不会无条件的崩溃
异常能够编译通过, 但是不能运行成功; 而语法错误不能编译成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 print (1 / 0 )File "../5-读文件.py" , line 22 , in <module> print (1 / 0 ) ZeroDivisionError: division by zero print (1 + "12" )File "../5-读文件.py" , line 22 , in <module> print (1 + "12" ) TypeError: unsupported operand type (s) for +: 'int' and 'str' print (1 + ad * 2 )File "../5-读文件.py" , line 22 , in <module> print (1 + ad * 2 ) NameError: name 'ad' is not defined
错误信息的第一行, 指出了异常出现的文件和错误行
第二行, 提示了是哪一条语句出现了错误
第三行, 指出了是哪一种异常信息;异常也有不同的类型,异常类型做为错误信息的一部分显示出来
以上三种异常分别为: 零除错误(ZeroDivisionError
), 类型错误(TypeError
) 和 命名错误(NameError
)
相关异常信息官方文档
异常处理
我们都知道, 正常情况下, 程序执行过程中遇到错误或者异常, 程序便会中断执行, 这也就以为着后面的程序将无法执行
但是在Python
中, 我们可以针对异常做出一些处理, 使之在遇到异常错误时, 继续执行后面的代码
异常类其实是class
类, 所有的错误都是继承自BaseException
注意: 还有一些错误是无法跳过的, 比如内存错误
第一种格式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 try : 语句t except 错误码 as e: 语句1 except 错误码 as e: 语句2 except 错误码 as e: 语句3 ... except 错误码 as e: 语句n else : 语句e
语句中的else
是可有可无的
except
语句中的as e
也可以不加
1 2 3 4 5 6 7 8 9 10 11 12 try .......except ....else try : 语句t except 错误码1 : 语句1 except 错误码2 : 语句2 else : 语句3
第二种格式 一个 except
子句可以在括号中列出多个异常的名字, 对于指定的一些异常做统一处理
1 2 3 4 try : print (7 / 0 ) except (ZeroDivisionError, NameError): print ('程序异常' )
第三种格式 无论遇到的是哪一种异常, 均做统一处理
1 2 3 4 try : print (7 / 0 ) except : print ('程序异常' )
try
语句工作方式
首先,执行 try
子句 (在 try
和 except
关键字之间的部分)
如果没有异常发生, except
子句 在 try
语句执行完毕后就被忽略了
如果在 try
子句执行过程中发生了异常,那么该子句其余的部分就会被忽略
如果异常匹配于 except
关键字后面指定的异常类型,就执行对应的except
子句。然后继续执行 try
语句之后的代码
如果发生了一个异常,在 except
子句中没有与之匹配的分支,它就会传递到上一级 try
语句中
如果最终仍找不到对应的处理语句,它就成为一个 未处理异常,终止程序运行,显示提示信息
使用示例: 一个 try
语句可能包含多个 except
子句,分别指定处理不同的异常。至多只会有一个分支被执行。异常处理程序只会处理对应的 try
子句中发生的异常,在同一个 try
语句中,其他子句中发生的异常则不作处理
1 2 3 4 5 6 7 8 try : print (7 / 0 ) except ZeroDivisionError: print ('除数为0' ) except NameError: print ('没有改变量' ) except SyntaxError: print ('不知道' )
一个 except
子句可以在括号中列出多个异常的名字
1 2 3 4 try: print(7 / 0 ) except (ZeroDivisionError, NameError): print('程序异常' )
最后一个 except
子句可以省略异常名称,以作为通配符使用。你需要慎用此法,因为它会轻易隐藏一个实际的程序错误!可以使用这种方法打印一条错误信息,然后重新抛出异常(允许调用者处理这个异常):
1 2 3 4 5 6 7 8 9 10 11 try : f = open ('myfile.txt' ) s = f.readline() i = int (s.strip()) except OSError as err: print ("OS error: {0}" .format (err)) except ValueError: print ("Could not convert data to an integer." ) except : print ("Unexpected error:" , sys.exc_info()[0 ]) raise
try … except
语句可以带有一个 else子句,该子句只能出现在所有 except 子句之后。当 try 语句没有抛出异常时,需要执行一些代码,可以使用这个子句。例如:
1 2 3 4 5 6 7 8 try : print (7 / 0 ) except ZeroDivisionError: print ('除数为0' ) except NameError: print ('没有改变量' ) else : print ('代码OK' )
抛出异常 raise
语句允许程序员强制抛出一个指定的异常
1 2 3 4 5 6 7 raise NameError('TitanJun' )Traceback (most recent call last): File "../1-异常处理.py" , line 95 , in <module> raise NameError('TitanJun' ) NameError: TitanJun
如果你需要明确一个异常是否抛出,但不想处理它,raise
语句可以让你很简单的重新抛出该异常:
1 2 3 4 5 6 7 8 9 10 11 12 try : raise NameError('TitanJun' ) except NameError: print ('NameError错误' ) raise NameError错误 Traceback (most recent call last): File "../1-异常处理.py" , line 97 , in <module> raise NameError('TitanJun' ) NameError: TitanJun
定义清理行为
try
语句还有另一个可选的子句: try--except--finally
,目的在于定义在任何情况下都一定要执行的功能
不管有没有发生异常,finally
子句 在程序离开 try
后都一定会被执行
当 try
语句中发生了未被 except
捕获的异常(或者它发生在 except
或 else
子句中),在 finally
子句执行完后它会被重新抛出。 - try
语句经由 break
,continue
或 return
语句退 出也一样会执行 finally
子句
1 2 3 4 5 6 7 8 9 10 11 12 try : print (7 / 0 ) except ZeroDivisionError: print ('除数为0' ) except NameError: print ('没有改变量' ) finally : print ('我一定要执行' ) 除数为0 我一定要执行
预定义清理行为 有些对象定义了标准的清理行为,无论对象操作是否成功,不再需要该对象的时候就会起作用。以下示例尝试打开文件并把内容打印到屏幕上
1 2 for line in open ("myfile.txt" ): print (line)
这段代码的问题在于在代码执行完后没有立即关闭打开的文件。这在简单的脚本里没什么,但是大型应用程序就会出问题。with
语句使得文件之类的对象可以 确保总能及时准确地进行清理
1 2 3 with open ("myfile.txt" ) as f: for line in f: print (line)
语句执行后,文件 f
总会被关闭,即使是在处理文件中的数据时出错也一样。其它对象是否提供了预定义的清理行为要查看它们的文档
文件读写
在Python
中文件的读写, 通常以文本打开,这意味着,你从文件读出和向文件写入的字符串会被特定的编码方式(默认是UTF-8)编码。
模式后面的 'b'
以 二进制模式 打开文件:数据会以字节对象的形式读出和写入。
这种模式应该用于所有不包含文本的文件
读取文件 函数 open()
返回文件对象,通常的用法需要两个参数
1 2 3 4 5 def open (file, mode='r' , buffering=None , encoding=None , errors=None , newline=None , closefd=True )path = r'/Users/xxx/text.txt' file = open (path, 'r' )
参数一: 一个含有文件名的字符串
参数二: 描述如何使用该文件的字符串, 默认为 'r'
'r'
: 时表示只是读取文件
'rb'
: 以二进制形式打开一个文件用于只读, 文件描述放在文件的开头
'w'
: 表示只是写入文件(已经存在的同名文件将被删掉)
'wb'
: 打开一个文件用于写入二进制, 如果该文件已经存在会覆盖, 如果不存在则创建新文件
'w+'
: 打开一个文件用于读写
'a'
: 表示打开文件进行追加,写入到文件中的任何数据将自动添加到末尾
'r+'
: 表示打开文件进行读取和写入
'b'
: 以 二进制模式 打开文件
文件对象方法 read()
要读取文件内容,需要调用 file.read(size)
,该方法读取若干数量的数据并以字符串形式返回其内容
size
是可选的数值,指定字符串长度, 如果没有指定 size
或者指定为负数,就会读取并返回整个文件。
当文件大小为当前机器内存两倍时,就会产生问题。反之,会尽可能按比较大的 size
读取和返回数据。
如果到了文件末尾,file.read()
会返回一个空字符串
1 2 3 str = file.read()print (str )
readline()
file.readline()
从文件中读取单独一行,字符串结尾会自动加上一个换行符(\n
),只有当文件最后一行没有以换行符结尾时,这一操作才会被忽略。
这样返回值就不会有混淆,如果 file.readline()
返回一个空字符串,那就表示到达了文件末尾,如果是一个空行,就会描述为 '\n'
,一个只包含换行符的字符串
遍历文件对象 可以循环遍历文件对象来读取文件中的每一行。这是一种内存高效、快速,并且代码简介的方式
1 2 for line in file: print(line)
如果你想把文件中的所有行读到一个列表中,你也可以使用 `list(file)` 或者 `file.readlines()`
1 2 3 print (list (file))print (file.readlines())
写入文件
write
: 将 string
的内容写入文件,并返回写入字符的长度
writelines
: 用于向文件中写入一序列的字符串, 没有返回值
1 2 3 4 5 6 7 8 leng = file.write('我是一只小鸭子' ) print (leng)file.writelines(['hello' , 'Python' ])
tell/seek
tell
: 返回一个整数,代表文件对象在文件中的指针位置,该数值计量了自文件开头到指针处的比特数。
需要改变文件对象指针话话,使用 file.seek(offset,from_what)
。
指针在该操作中从指定的引用位置移动 offset
比特,引用位置由 from_what
参数指定。
from_what
值为 0 表示自文件起始处开始,1 表示自当前文件指针位置开始,2 表示自文件末尾开始。
from_what
可以忽略,其默认值为零,此时从文件头开始
1 2 3 4 5 6 7 8 9 l = file.readline() print (l)pos = file.tell() print (pos)b'https://www.titanjun.top/\n' 26
重新设置文件读取指针到开头
1 2 3 4 5 6 file.seek(5 , 0 ) print (file.readline())b'https://www.titanjun.top/\n' b'://www.titanjun.top/\n'
close
当你使用完一个文件时,调用 file.close()
方法就可以关闭它并释放其占用的所有系统资源。
在调用 file.close()
方法后,试图再次使用文件对象将会自动失败
1 2 3 4 5 6 7 file.close() file.read() File "../5-读文件.py" , line 64 , in <module> file.read() ValueError: read of closed file
关键字with
用关键字 with
处理文件对象是个好习惯。
它的先进之处在于文件用完后会自动关闭,就算发生异常也没关系。
它是 try-finally
块的简写
1 2 3 4 with open (path, 'rb+' ) as file: str = file.read() print (str ) file.close()