⛄异常处理

编写计算机程序时,通常能够区分正常异常(不正常)情况。异常事件可能是错误(如试图除以零),也可能是通常不会发生的事情。为处理这些异常事件,可在每个可能发生这些事件的地方都使用条件语句。例如,对于每个除法运算,都检查除数是否为零。然而,这样做不仅效率低下、缺乏灵活性,还可能导致程序难以卒读。你可能很想忽略这些异常事件,希望它们不会发生,但Python提供功能强大的替代解决方案——异常处理机制。

Python使用异常对象来表示异常状态,并在遇到错误时引发异常。异常对象未被处理(或捕获)时,程序将终止并显示一条错误消息(traceback)。

In [1]:1/0
Traceback (most recent call last):
File "<ipython-input-2-9e1622b385b6>", line 1, in <module>
1/0
ZeroDivisionError: division by zero

如果异常只能用来显示错误消息,就没多大意思了。但事实上,每个异常都是某个类的实例。你能以各种方式引发和捕获这些实例,从而逮住错误并采取措施,而不是放任整个程序失败。

👀异常的概念

  • 程序在运行时,如果Python解释器遇到到一个错误,会停止程序的执行,并且提示一些错误信息,这就是异常
  • 程序停止执行并且提示错误信息这个动作,我们通常称之为:抛出(raise)异常

程序开发时,很难将所有的特殊情况,都处理的面面俱到,通过异常捕获可以针对突发事件做集中的处理,从而保证程序的稳定性和健壮性

👀捕获异常

(1)简单的捕获异常语法

在程序开发中,如果对某些代码的执行不能确定是否正确,可以增加try(尝试)捕获异常

# 捕获异常最简单的语法格式
try:
# 尝试执行的代码
except:
# 出现错误的处理
  • try尝试,下方编写要尝试代码,不确定是否能够正常执行的代码。
  • except如果不是,下方编写尝试失败的代码。
try:
# 提示用户输入一个数字
num = int(input("请输入数字:"))
except:
print("请输入正确的数字")

# 输出结果样例
请输入数字:1

请输入数字:a
请输入正确的数字

(2)错误类型捕获及完整语法

在程序执行时,可能会遇到不同类型的异常,并且需要针对不同类型的异常做出不同的响应,这个时候,就需要捕获错误类型了。

捕获未知错误

  • 在开发时,要预判到所有可能出现的错误,还是有一定难度的。
  • 如果希望程序无论出现任何错误,都不会因为Python解释器抛出异常而被终止,可以再增加一个except
# 语法
except Exception as result:
print("未知错误%s"%result)

异常捕获完整语法:

# 语法
try:
# 尝试执行的代码
pass
except 错误类型1:
# 针对错误类型1,对应的代码处理
pass
except(错误类型2, 错误类型3):
# 针对错误类型2和3,对应的代码处理
pass
except Exception as result:
# 捕获未知错误
print("未知错误%s"%result)
else:
# 只有没有异常时才会执行的代码
finally:
# 无论是否有异常,都会执行的代码

完整捕获异常的例子:

try:
# 提示用户输入一个数字
num = int(input("请输入数字:"))
result = 8 / num
print(result)
except ValueError:
print("请输入正确的数字")
except ZeroDivisionError:
print("除0错误")
except Exception as result:
# 捕获未知错误
print("未知错误%s"%result)
else:
print("正常执行")
finally:
print("执行完成,但是不保证正确")

👀异常的传递

  • 异常的传递——当函数/方法执行出现异常,会将异常传递给函数/方法的调用一方;
  • 如果传递到主程序,仍然没有异常处理,程序才会被终止。
  • 在开发中,可以在主函数中增加异常捕获
  • 而在主函数中调用的其他函数,只要出现异常,都会传递到主函数的异常捕获中;
  • 这样就不需要在代码中,增加大量的异常捕获,能够保证代码的整洁和健壮性。
def demo1():
return int(input("请输入一个整数:"))

def demo2():
return demo1()

try:
print(demo2())
except ValueError:
print("请输入正确的数字")
except Exception as result:
# 捕获未知错误
print("未知错误%s"%result)

👀抛出raise异常

应用场景

  • 在开发中,除了代码执行出错Python解释器会抛出异常之外;
  • 还可以根据应用程序特有的业务需求主动抛出异常

示例

  • 提示用户输入密码,如果长度少于8,抛出异常。

注意

  • 当前函数只负责提示用户输入密码,如果密码长度不正确,需要其他的函数进行额外处理;
  • 因此可以抛出异常,由其他需要处理的函数捕获异常

抛出异常

  • Python中提供了一个Exception 异常类。

  • 在开发时,如果满足特定业务需求时,希望抛出异常,可以:

    • 创建一个Exception的对象

    • 使用raise关键字抛出异常对象

def input_passward():
# 提示输入密码
pwd = input("请输入密码:")
# 判断,如果密码长度≥8,返回用户输入的密码
if len(pwd) >= 8:
return pwd
# 如果密码长度<8,主动抛出异常
print("主动抛出异常")
# 1.创建异常对象,可以使用错误信息字符串作为参数
ex = Exception("密码长度不够")
# 2.抛出异常
raise ex

try:
print(input_passward())
except Exception as result:
print("异常为:%s" % result)

# 输出结果样例
请输入密码:123
主动抛出异常
异常为:密码长度不够

⛄模块和包(Package)

👀模块

(1)模块的概念

模块是Python程序架构的一个核心概念

  • 每一个以扩展名py结尾的Python源代码文件都是一个模块,文件名就是模块名
  • 模块名同样也是一个标识符,需要符合标识符的命名规则(字母、数字、下划线,不能以数字开头);
  • 在模块中定义的全局变量函数都是提供给外界直接使用的工具;
  • 模块就好比是工具包,要想使用这个工具包中的工具,就需要先导入这个模块。

(2)模块导入方式

import导入

import 模块名1, 模块名2

提示:在导入模块时,PEP8建议每个导入应该独占一行

import 模块名1
import 模块名2

导入之后,通过模块名.使用模块提供的工具–全局变量、函数、类。如果模块的名字太长,可以使用as指定模块的名称,以方便在代码中的使用。

import 模块名 as 模块别名

注意:模块别名应该符合大驼峰命名法

from..import导入

  • 如果希望从某一个模块中,导入部分工具,就可以使用from..import的方式;
  • import 模块名一次性把模块中所有工具全部导入,并且通过模块名/别名访问。
# 从模块导入某一个工具
from 模块名1 import 工具名

导入之后

  • 不需要通过模块名.
  • 可以直接使用模块提供的工具——全局变量、函数、类。

注意

如果两个模块,存在同名的函数,那么后导入模块的函数,会覆盖掉先导入的函数

  • 开发时import代码应该统一写在代码的顶部,更容易及时发现冲突;
  • 一旦发现冲突,可以使用as关键字给其中一个工具起一个别名

from...import * (知道)

# 从模块导入所有工具
from 模块名1 import *

注意:这种方式不推荐使用,不需要通过模块名.调用,因为函数重名并没有任何提示,出现问题不好排查。

(3)模块的搜索顺序

Python的解释器在导入模块时,会:

  • 搜索当前目录指定模块名的文件,如果有就直接导入;
  • 如果没有,再搜索系统目录

在开发时,给文件起名,不要和系统的模块文件重名

Python中每一个模块都有一个内置属性__file__可以查看模块的完整路径

import random
# 生成一个0~18的数字
rand = random.randint(e,10)
print(rand)

注意:如果当前目录下,存在一个random.py的文件,程序就无法正常执行了!这个时候,Python的解释器会加载当前目录下的random.py而不会加载系统的random模块。

(4)每一个文件都应该是可以被导入的

  • 一个独立的Python文件就是一个模块
  • 在导入文件时,文件中所有没有任何缩进的代码都会被执行一遍!

实际开发场景

  • 在实际开发中,每一个模块都是独立开发的,大多都有专人负责;
  • 开发人员通常会在模块下方增加一些测试代码,仅在模块内使用,而被导入到其他文件中不需要执行。

__name__属性

  • __name__属性可以做到,测试模块的代码只在测试情况下被运行,而在被导入时不会被执行
  • __name__Python的一个内置属性,记录着一个字符串;
  • 如果是被其他文件导入的,__name__就是模块名;
  • 如果是当前执行的程序,__name____main__

​ 在很多Python文件中都会看到以下格式的代码:

# 导入模块
# 定义全局变量
# 定义类
# 定义函数

# 在代码的最下方
def main():
#...
pass
# 根据_name判断是否执行下方代码
if __name__ == "__main__":
main()

👀包(Package)

  • 是一个包含多个模块特殊目录
  • 目录下有一个特殊的文件__init__.py
  • 包名的命名方式和变量名一致,小写字母、下划线等。

好处:使用import 包名可以一次性导入包中所有的模块。

__init__.py,要在外界使用包中的模块,需要在__init__.py中指定对外界提供的模块列表。

# 从当前目录导入模块列表
from . import 模块名1
from . import 模块名2

⛄文件处理

👀文件的概念

  • 计算机的文件,就是存储在某种长期储存设备上的一段数据。
  • 文件的存储方式:是以二进制的方式保存在磁盘上。

👀文件的基本操作

文本文件和二进制文件,在计算机中操作文件的套路非常固定,一共包含三个步骤:

  • 打开文件

  • 读、写文件

    • 读,将文件内容读入内存

    • 写,将内存内容写入文件

  • 关闭文件

Python中要操作文件需要记住1个函数和3个方法

序号 函数/方法 说明
01 open 打开文件,并且返回文件操作对象
02 read 将文件内容读取到内存
03 write 将指定内容写入文件
04 close 关闭文件

open函数负责打开文件,并且返回文件对象;

read/write/close三个方法都需要通过文件对象调用。

👀read()方法

  • open函数的第一个参数是要打开的文件名(文件名区分大小写)。

    • 如果文件存在,返回文件操作对象;

    • 如果文件不存在,会抛出异常。

  • read方法可以一次性读入返回文件的所有内容

  • close方法负责关闭文件

    • 如果忘记关闭文件,会造成系统资源消耗,而且会影响到后续对文件的访问。
  • 注意:read方法执行后,会把文件指针移动到文件的末尾

# 1.打开-文件名需要注意大小写
file = open("README")
# 2.读取
text = file.read()
print(text)
# 3.关闭
file.close()

提示:在开发中,通常会先编写打开和关闭的代码,再编写中间针对文件的读/写操作!使用 with 关键字系统会自动调用 f.close() 方法, with 的作用等效于 try/finally 语句是一样的。

# 打开文件
filename=open("file.txt")
# 读取文件内容
text = filename.read()
print(text)
# 如果执行了一次read方法,读取了所有内容,那么再次调用read方法,还能够获得到内容吗?读取不到
# 当执行了read方法后,文件指针会移动到读取内容的末尾
text1 = filename.read()
print(text1)
# 关闭文件
filename.close()

文件指针(知道)

  • 文件指针标记从哪个位置开始读取数据
  • 第一次打开文件时,通常文件指针会指向文件的开始位置
  • 当执行了read方法后,文件指针会移动到读取内容的末尾
    • 默认情况下会移动到文件末尾

思考:

如果执行了一次read方法,读取了所有内容,那么再次调用read方法,还能够获得到内容吗?

不能,第一次读取之后,文件指针移动到了文件末尾,再次调用不会读取到任何的内容。

👀打开文件的方式

open函数默认以只读方式打开文件,并且返回文件对象语法如下:

f = open("文件名","访问方式")

访问方式说明

访问方式 说明
r 以只读方式打开文件。
文件的指针将会放在文件的开头,这是默认模式。如果文件不存在,抛出异常。
w 以只写方式打开文件。
如果文件存在会被覆盖。如果文件不存在,创建新文件。
a 以追加方式打开文件。
如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入。
r+ 以读写方式打开文件。
文件的指针将会放在文件的开头。如果文件不存在,抛出异常。
w+ 以读写方式打开文件。
如果文件存在会被覆盖。如果文件不存在,创建新文件。
a+ 以读写方式打开文件。
如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入。

提示:频繁的移动文件指针,会影响文件的读写效率,开发中更多会以只读、只写的方式来操作文件。

# 写入文件
f = open("README", "w")

f.write("hello python \n")
f.write("我是谁")

# 关闭文件
f.close()

👀按行读取文件内容

  • read方法默认会把文件的所有内容一次性读取到内存
  • 如果文件太大,对内存的占用会非常严重。

readline方法

  • readline方法可以一次读取一行内容;
  • 方法执行后,会把文件指针移动到下一行,准备再次读取。
# 读取大文件的正确方法
# 打开文件
file=open("README")
while True:
# 读取一行内容
text=file.readline()

# 判断是否读到内容
if not text:
break

# 每读取一行的末尾已经有了一个'\n'
print(text,end="")
# 关闭文件
file.close()

👀文件/目录常用管理操作os

  • 终端/文件浏览器中可以执行常规的文件/目录管理操作,例如:
    • 创建、重命名、删除、改变路径、查看目录内容、……
  • Python中,如果希望通过程序实现上述功能,需要导入os模块。

文件操作

序号 方法名 说明 示例
01 rename 重命名文件 os.rename(源文件名,目标文件名)
02 remove 删除文件 os.remove(文件名)

目录操作

序号 方法名 说明 示例
01 listdir 目录列表 os.listdir(目录名)
02 mkdir 创建目录 os.mkdir(目录名)
03 rmdir 删除目录 os.rmdir(目录名)
04 getcwd 获取当前目录 os.getcwd()
05 chdir 修改工作目录 os.chdir(目标目录)
06 path.isdir 判断是否是文件 os.path.isdir(文件路径)

提示:文件或者目录操作都支持相对路径绝对路径。其他操作,可以具体了解os模块详细内容。

文本文件的编码格式(科普)。文本文件存储的内容是基于字符编码的文件,常见的编码有ASCII编码,UNICODE编码等。

  • Python 2.x默认使用ASCII编码
  • Python 3.x默认使用UTF-8编码

unicode字符串

  • Python2.x中,即使指定了文件使用UTF-8的编码格式,但是在遍历字符时,仍然会以字节为单位遍历字符串;
  • 要能够正确的遍历字符串,在定义字符串时,需要在字符串的引号前,增加一个小写字母u,告诉解释器这是一个unicode字符串(使用UTF-8编码格式的字符串)。

eval()函数

eval()函数十分强大,将字符串当成有效的表达式来求值并返回计算结果,但是不能滥用。

# 基本的数学计算
print(eval("1+1"))
# 字符串重复
print(eval("'*'*10"))
# 将字符串转换成列表
print(type(eval("[1,2,3,4,5]")))
# 将字符串转换成字典
print(type(eval("{'name':'xiaoming','age':18}")))

# 输出结果
2
**********
<class 'list'>
<class 'dict'>

笔者不才,请多交流!!!

参考文献:黑马程序员《Python入门教程完整版》