在开始正题之前,让我们先来看看本次学习的重难点。本次的知识点可以分为三个部分:
(1)常见的语法错误。
(2)基础语法中的常见异常。
(3)操作文件过程中的常见异常,这部分也是重难点知识。
编写代码时经常会遇到错误,因此掌握本课的内容对你将来自己做项目非常有帮助。至少可以节省大量排查错误的时间。加油!
- 常见的语法错误
下面图片中显示的情况,相信你在平时写代码的过程中,肯定已经遇到过不少次了。
如果程序能够顺利运行,是不会出现这样的消息的。
这些在终端显示的"错误消息"表示我们在编写或运行程序时可能出现了问题。通常需要在Python中进行异常处理,以确保程序能够正确运行。
“异常处理”这个名词你应该是第一次接触,它包括我们在程序运行前的处理和在程序运行过程中的处理。
在Python中,异常处理是一门比较深奥的学问,涉及到很多特有的机制。不过我们暂时不需要掌握那么深入的知识,为了方便理解,你可以将解决问题或处理"错误消息"的过程称为"异常处理"。
不过,我们还不需要掌握这么难的知识,为了方便理解,你可以把解决问题或处理“错误消息”的过程称为“异常处理”。
我相信你凭借现有的编程经验,也能发现"错误消息"不仅限于图片中显示的那一种。实际上,按照Python官方文档的定义,这些"错误消息"至少可以分为两类:语法错误(syntax errors)和异常(exceptions)。
1.1 基本概念
**语法错误(syntax errors)**是初学者最容易犯的错误,简单来说就是代码不符合Python的基本语法规范,导致程序出错。
当你的代码完全符合Python的语法规范后,就可以尝试运行程序了。但是在程序运行过程中,仍然可能出现问题,我们称这类问题为异常。
作为一个有经验的人,为了让你少走弯路,我将根据你的技术水平列举一些常见的语法错误和异常。当然,我还会讲解它们产生的原因以及相应的解决办法。
此外,我还总结了一些在操作文件过程中可能遇到的常见异常,希望你引以为戒,尽量避免这些问题。
需要强调的是,本次讲解的语法错误和异常并不能代表全部,实际上,这只是冰山一角,还有许多复杂且棘手的异常等待你以后慢慢解决。
但是不要害怕,这是提高编程水平的必经之路。送你八个字:“遇事不慌,问题不大。”保持积极的心态是最重要的。
好了,按顺序,我们先来学习常见的语法错误。
正如前面所述,**语法错误(syntax errors)**是初学者最容易犯的错误,如果代码不符合 Python 的语法规范,比如漏了冒号,混用中英文符号等,就会出现这种错误。
如下图所示:
想要解决问题,最基本动作就是查看错误消息,终端会显示出现语法错误的那一行,并显示一个箭头^,指向这行里面检测到的第一个错误。
例如,在上图中,line 1和SyntaxError告诉你第一行有语法错误,出错位置很可能是在print() 附近。
需要说明的是,行数告诉你代码出现问题的位置,但不一定是最初出错的地方。有时候,真正的错误可能出现在提示信息所指示的位置之前,通常在前一行。
知道错误出现的位置后,就可以尝试解决错误了。下面我将分别讲解两种常见的语法错误:SyntaxError 和 IndentationError,以及它们产生的原因和相应的解决办法。
1.2 SyntaxError
第一种:SyntaxError: invalid syntax(无效语法)
在Python中,"SyntaxError: invalid syntax"是最常见的语法错误之一。它通常由以下几种情况引起:
一、遗漏了标点符号。
在第一种情况下,我们需要检查是否有遗漏标点符号。例如,在函数定义时,是否忘记了在括号后面加上冒号;在if条件判断和while循环中的条件后面是否有冒号;在for循环的可迭代对象后是否有冒号;以及在字典中的每个键值对之间是否有逗号等等。
此外,还要注意区分等于号(=)和等于比较运算符(==)的使用。
二、关键字拼写错误或遗漏。
第二种情况,很有可能是因为你手抖或者粗心,将 while 写成了 whlie,将 def 写成了 dfe 之类的错误,或者可能是你将 for…in… 中的关键字 in 忘写了。
三、变量名或函数名使用了关键字。
例:
# class 是 Python 的关键字,不可充当变量名
class = False
# True 是 Python 关键字,不可作为函数名
def True():
print('好好学习,天天向上')
正如代码里注释写的,class 和 True 都是 Python 中的关键字,关键字是不可以用来作为变量名、函数名或者类名的。
Python 还有以下关键字(看一眼就好,不用记住)。
大多数开发环境都会使用不同的颜色来显示关键字,因此当不小心使用关键字作为变量名时,会看到颜色变化,从而知道需要修改变量名。显然,上述代码问题的解决办法是将class和True 替换为其他非关键字的变量名。类似的情况都可以用这种方法解决。
第二种:SyntaxError: invalid character in identifier(标识符中有无效字符)
请观察下面的代码是否存在问题:
member_list = ['月娜', '廖雨', ‘王晴’,‘陈知枫’]
def member_printer():
print(member_list)
如果运行上面的代码,就会报 SyntaxError: invalid character in identifier 这个错误。
仔细观察,你会发现代码中使用了大量的中文符号,比如中文括号、中文逗号、中文冒号和中文引号。这可能是因为在编写代码时频繁切换中英文输入法所导致的。
了解了问题的原因,解决方法就呼之欲出了:将我标记出来的中文标点修改为对应的英文符号即可。
过于简单,就不让你改了。
第三种:SyntaxError: EOL while scanning string literal(检查到不完整的字符串)
这种情况通常是因为遗漏了字符串两边的引号,或者混用了引号(即字符串两边的引号不统一)。
请看下图:
解决办法也很简单,仔细检查字符串两头的引号是否有遗漏,而且需要记住,单引号和单引号匹配,双引号和双引号匹配,三引号和三引号匹配。
图片总结一下:
1.3 IndentationError
第二个要介绍的常见语法错误就是 IndentationError,即缩进错误。
在Python中,冒号下面的缩进帮助程序区分代码之间的层次结构,这些不同的层次表示代码运行的逻辑和先后顺序。
但如果在需要缩进的语句下面没有使用正确的缩进层级,甚至没有缩进,程序就会报错。
这次我主要讲两种常见的缩进错误。
第一种:IndentationError: expected an indented block(需要缩进的代码块)
请你观察下面的代码中第二和第三行。
member_list = ['月娜', '陈知枫']``if '陈知枫' in member_list:``print('陈知枫正在学习Python')
如果运行上述代码,会得到如下错误提示:
造成上面错误的原因很简单,print(‘陈知枫正在学习Python’) 前面没有缩进。
在缺少缩进的情况下,if条件判断和需要执行的print()语句会被视为两个独立的语句,可以理解为它们处于"平行关系"。
这样,if条件判断下面就没有可执行的语句了。无论条件是否成立,都不会有任何相应的操作,因此该条件判断失去了存在的意义。
修复这种缩进错误的方法也很简单:根据代码的运行顺序和逻辑,在需要缩进的地方(例如:if条件判断、for循环、while循环、def函数定义、class类的创建等)加入缩进即可。
修复后的代码运行结果如下。
第二种:IndentationError: unindent does not match any outer indentation level(缩进内容不匹配任何一个层级)
例:
a = 0
while a < 5:
if a == 3:
break
a += 1
print(a)
代码中的最后一行,print(a) 无法找到属于它的代码块。即 print(a) 要么和 if 条件判断平级,要么直接与 while 循环平级。
它现在的缩进层级使 Python 无法识别出它到底属于哪一部分,这是在难为 Python 啊。
要让上述代码顺利运行,需要将 **print(a)**调整到与if条件判断平级,这样程序就会在每次循环后打印a的值。或者将 print(a) 调整到与while循环平级,这样程序就会在最后一次循环后打印a的值。
IndentationError 总结
要解决缩进错误,最重要的是分析代码的逻辑,然后确定代码块的层级关系,从而确定缩进的层级。
1.4 总结
常见的语法错误总结
我们主要讲了三种 SyntaxError:
1)invalid syntax(无效语法)
2)invalid character in identifier(标识符中有无效字符)
3)EOL while scanning string literal(检查到不完整的字符串)
两种 IndentationError:
1)expected an indented block(需要缩进的代码块)
2)unindent does not match any outer indentation level(缩进内容不匹配任何一个层级)
其实,对于语法错误,只要我们细心一点,多留意一些注意事项就可以了,给你提供一份自检清单:
2. 基础语法中常见的异常
上面我说过,在程序运行过程中发生的问题,可以被称为异常。
下面我会先讲几个新手在学习 Python 基础语法过程中会遇到的异常,然后再讲几个你在操作文件的过程中会遇到的异常。
2.1 TypeError
第一个要介绍的是基础语法中常见的异常:TypeError,即类型错误。
你可能会很疑惑,明明出现了错误这两个字,为什么还要叫异常。确实有些诡异,但 Python 就是这么规定的,这种在程序运行过程中出现的错误,就得称为异常。
有一种很直观的方法可以帮助你来区分它们——观察终端显示的“错误消息”。
一般情况下,异常的“错误消息”会显示Traceback (most recent call last),而语法错误就不会显示。
回到 TypeError,通俗来说,发生这种异常的原因往往是使用的数据类型做了不太适合它自己的事。
下面我来列举三种较为常见的类型错误。
第一种:TypeError: unsupported operand type(s) for …(不支持的运算)
下面的代码就会引发这样的异常。
a = 1
b = '10.0'
c = '5'
print(a * (b - c))
代码的“错误消息”如图所示。
代码的第三行,将字符串类型的数据 ‘10.0’ 和字符串类型的数据 ‘5’ 相减,发生了错误。
要修改代码中的问题,你可以使用 int() 将 c 转换为整数类型,同时使用 float() 将 b 转换为浮点数类型。
当字符串类型的数据与数字相互运算时,需要进行类型转换才能确保程序顺利运行。类型转换时需要使用 int() 或 float()。
灵活使用 int() 和 float() 是处理这个异常的关键。
b是字符串类型的浮点数,所以需要使用 float(b) 将 b 转换为浮点类型。
c是字符串类型的整数,要使用 int© 将 c 转换为整数类型。
由于需要使用了 int() 和 float(),这会造成代码中存在很多括号 ()
,你一定要注意括号是否能一一对应,避免遗漏括号或有多余括号的情况出现。
来看看我是如何修改的。
a = 1
b = '10.0'
c = '5'
print(a * (float(b) - int(c)))
第二种:TypeError: can only concatenate str (not “int”) to str (只能用字符串拼接字符串)
顾名思义,这个 TypeError 是由不恰当的字符串拼接引起的。观察下面的代码。
living_expenses = 1500
print('陈知枫在学校的生活费是:' + living_expenses + '元')
代码的“错误消息”如图所示。
iving_expenses 是整数类型 int,但在第二行,**'陈知枫在学校的生活费是:****‘和’元’**都是字符串类型 str,我们是不能将整数类型和字符串类型的数据直接连接的。
所以处理这种异常的方法就是使用 str(),将非字符串数据转换为字符串,然后再进行拼接。
上面的代码修复后,运行结果如图所示。
第三种:TypeError: ‘xxx’ object is not iterable(对象不可被迭代)
例:
living_expenses = 1500
for number in living_expenses:
print(number)
运行后,会出现错误信息,TypeError: ‘int’ object is not iterable。这是因为代码尝试遍历一个整数类型的数据,但是整数类型不是一个可迭代对象。
还记得吗?for 循环只能循环遍历可迭代对象。
目前为止,你见过的可迭代对象应该包括:字符串类型,列表,元组和字典。剩下的一些类型,比如:int 类型,float 类型,布尔类型和 None 就不是可迭代对象。
请你一定要记住,使用 for 循环去遍历一个数据时,一定要确保其是可迭代对象。
总结
以上就是我介绍的三种常见的 TypeError:不支持的运算,只能用字符串拼接字符串,还有对象不可被迭代。
一句话总结:为了避免类型错误,你需要确定自己使用的数据类型是什么(可以借助 type()),需要进行的操作是什么,从而去确认是否需要使用函数来进行类型转换。
2.2 IndexError
IndexError 这个异常,和列表取值有关。
出现频率较高的索引错误是 IndexError: list index out of range(索引超出了范围)
例:
member_list = ['月娜', '廖雨', '王晴', '陈知枫']``print(member_list[4])
代码的问题就出在 member_list[4],列表的索引最大值是 3,但代码里却写了 4,导致程序运行时出错。
通常,我们通过列表名[索引]的方式来获取列表中的元素。
列表的索引方向有两种:
从左往右:索引的最小值为 0
,最大值为列表长度 -1。
从右往左:索引的最小值为-
列表长度,最大值为 -1
。
如下图所示:
所以,在列表中使用索引取值时,一定要确保索引在正确的范围区间内。
3. 操作文件过程中的常见异常
在之前的课程中,我们学习了如何使用Python操作文件,现在让我们简单回顾一下相关知识点。
Python中的内置函数open()经常用于打开文件,并返回一个文件对象。
它有三个比较重要的参数,file 是需要传入文件的路径,mode 用来控制读写模式,还有 encoding 来控制文件的编码方式。
如果我们得到的是 csv 文件对象,可以使用 csv 模块的函数 DictReader(),该方法会将文件内容以字典形式返回。
如果我们得到的是一个CSV文件对象,可以使用csv模块的函数DictReader(),它会将文件内容以字典形式返回。要将内容写入文件,可以使用csv模块的函数DictWriter(),但需要将**open()**函数的mode参数设置为写模式或追加模式。
所有被打开的文件对象最后都应该被关闭,可以使用文件对象的方法**close()**来实现。
现在,我们正式开始学习操作文件过程中常见的异常。
3.1 ModuleNotFoundError
ModuleNotFoundError 未找到模块错误,是一种有关模块的异常
下面的代码都存在这类异常,你可以运行一下,观察出现的错误消息
import QS
import OS
import ps
之前我们接触过 Python 中的内置模块—— os 模块。在使用 os 模块时,需要先导入该模块。
但上面的三种写法都是错误的,会引发异常ModuleNotFoundError: No module named ‘xx’(没有名为xx的模块)。
因为上面的代码中都将 os 写错了,第一个虽然很像,但 QS 和 os 还是有区别的;第二个虽然写了 OS,但是正确的写法应该是小写的 os;第三个可能是因为手抖,在键盘上敲 o 的时候误触成了它旁边的 p。
解决这个异常的方法很简单,只需要在导入模块时仔细检查模块名的拼写是否正确即可。
3.2 AttributeError
正确导入os模块后,我们使用了它的方法os.listdir(path),来获取目标文件夹下的所有文件和文件夹的名称。
然而,如果在这一步出现问题,会导致什么样的错误呢?我们来探究一下。
让我们先运行下面的代码,观察错误消息:
import os
path = './'
files_list = os.dirlist(path)
print(files_list)
运行后,代码报错 AttributeEr****ror: module ‘os’ has no attribute ‘dirlist’。
翻译过来就是属性错误:
模块 ‘os’ 没有属性 ‘dirlist’。
问题已经很明显了,我们本应该使用的方法是 os.listdir(),但误将其写为 os.dirlist() 这个不存在的方法。这就导致了异常的产生。
为了避免这种异常,我们在使用模块中的方法时,需要仔细检查拼写是否正确,并确保所使用的模块或库中存在该方法或属性。
至于如何查找这些方法或属性,我推荐你去查阅要使用的模块或库的官方文档,或者你也可以在百度输入你想要使用的方法,去看看网络上的“大牛们”是怎么使用的。
3.3 FileNotFoundError
之前学习的函数 open(),如果使用不当,也有可能会产生一些异常,需要你特别注意。
比如它的第一个参数 file。
file 要求我们传入一个正确的文件路径,无论是绝对路径还是相对路径,只要确保这个路径是正确的即可。
但如果这个路径错误了,会发生什么呢。
假设在【工作】
文件夹下有 csv 文本文件 【demo.csv】
,但我们的代码却写成了下面的样子。
demo_file = open('./目标文件夹/demo.csv')
file_dict = csv.DictReader(demo_file)
for row in file_dict:
print(row)
demo_file.close()
请注意,代码的第一行错误地将文件路径写成了目标文件夹。这将导致出现下面图片中的异常:
错误消息 FileNotFoundError: [Errno 2] No such file or directory: ‘./目标文件夹/demo.csv’ 的意思就是:没有文件或文件夹的路径是 ‘./目标文件夹/demo.csv’。
总结一下,open() 函数打开一个文件,并且第二个参数是读取模式时(默认就是读取模式),若第一个参数的路径错误或者该路径指向的文件根本不存在,就会报文件找不到的错误。
因此,为了避免这种异常,我们需要根据路径找到相应的文件或文件夹,并检查文件或文件夹名是否正确。
3.4 UnicodeDecodeError
下面来讲最后的内容,也是 open() 函数的另一个参数 encoding 会引发的异常,很多新手在这个参数上容易“摔跟头”。
之前我们介绍的写法是encoding=‘utf-8’,因为大多数情况下,文件的编码格式都是’utf-8’。
但如果你需要使用别人分享的代码时,很有可能会因为你们之间使用了不同的编码格式而引发异常。
图片中显示的 csv 文件使用了 utf-8 格式的编码(你可以在 VSCode 的右下角查看到当前文件的编码格式,如图片中所示)。
下面的代码中的open函数使用了 gbk 格式 下面的代码来读取该文件,观察出现的错误消息
看见了吗?终端出现错误消息:
UnicodeDecodeError: ‘gbk’ codec can’t decode byte xxxx in position xx: invalid continuation byte。
这就是由编码格式不匹配引发的异常,要修复这个异常也很简单,写为 encoding=‘utf-8’ 即可。
以后遇到这样的异常,你有两种解决方法:
第一种,先利用 VSCode 等软件去查看文件的编码格式,然后再将文件的编码格式修改为你在代码中写的编码格式即可。
第二种,还是先在 VSCode 里面查看文件的编码格式,然后将代码中的 encoding 参数改为该文件的编码格式即可。
好了,操作文件过程中的常见异常就这么多了,总结图如下所示。
4. 知识总结
面对错误与异常的时候,一定要记住:不要慌!你要知道,程序出了问题,很大一部分原因是因为我们对代码的细节和实现方法思考得不够清楚。
当你不确定如何修改错误时,可以借助**print()**函数,将代码中关键步骤的结果打印出来,检查这些结果是否符合预期,以找出问题所在。
另外,如果你的代码总是出错,而且不清楚具体出错的地方,可以使用#号将后面的代码注释掉,然后逐行解除注释,逐行运行代码,以帮助你排除异常。
本教程讲解的内容虽然看似繁多,但实际上只是涉及到了一小部分适合初学者理解的、可能会在今后遇到的问题。不要觉得这些问题很简单,Python的世界非常广阔,随着你水平的提高,你将会遇到更具挑战性的问题。
来一起复习下知识点。
首先来看知识点汇总图:
再看看重难点部分。
本节的重难点在于操作文件过程中的常见异常。
首先是 ModuleNotFoundError 未找到模块错误,出现这种异常的时候,你需要检查模块名是否输入正确,想使用的模块是否存在。
第二个是 AttributeError 属性错误,该异常需要你检查使用的方法或属性的拼写是否正确,确认拼写无误后,如果还报错,就得检查模块或库中是否存在该方法或者属性。
接着是 FileNotFoundError 文件未找到错误,这个需要你确保传入的文件路径(绝对路径或相对路径)是存在并且正确的。
最后是 UnicodeDecodeError 解码错误,如果 open() 函数的参数 encoding 设置错误,就会引发这个异常。所以你需要搞清楚文件的编码格式,并且将正确的编码传给 encoding。
今天的学习内容到这里就结束啦,为你的坚持鼓个掌吧。加油,基础阶段也快结束了,期待你学习完成,可以继续下一个阶段内容的学习。