模块和包
模块
简而言之,在python中,一个文件(以“.py”为后缀名的文件)就叫做一个模块,每一个模块在python里都被看做是一个独立的文件。模块可以被项目中的其他模块、一些脚本甚至是交互式的解析器所使用,它可以被其他程序引用,从而使用该模块里的函数等功能,使用Python中的标准库也是采用这种方法。
导入模块的三种方式:
-
方式一:import 模块名
# 导入整个demo模块
import demo
# 使用demo模块名作为前缀访问模块中的Tiger类
t = demo.Tiger()
# 使用demo模块名作为前缀访问模块中的函数
demo.say()
导入整个模块时,也可以为模块指定别名:
# 导入整个demo模块,并更名为d的别名
import demo as d
t = d.Tiger()
d.say()
也可以一次导入多个模块,多个模块之间用逗号隔开:
# 导入demo,demo02两个模块
import demo,demo02
# 使用模块名访问模块中的成员
demo.say()
demo02.hello()
在导入多个模块的同时,也可以为模块指定别名: import demo as d,demo as d1
-
方式二:from 模块名 import 功能名
使用了 from...import 最简单的语法来导入指定成员:
# 导入demo模块中的指定成员
from demo import Tiger
t = Tiger()
print(Tiger.name)
导入模块成员时,也可以为成员指定别名:
# 导入demo模块的Tiger成员,并为其指定别名tg
from demo import Tiger as tg
t = tg()
-
方式三:from 模块名 import *
在Python项目下创建fun包,并在__init__.py
文件中配置包的导入行为。
# 导入fun包下中demo01模块下的Lion成员
from fun.demo01 import Lion
__all__ = ["Lion"]
只有以“from 模块名 import *”形式导入的模块,当该模块设有
__all__
变量时,只能导入该变量指定的成员,未指定的成员是无法导入的。
# 导入fun包
from fun import *
l = Lion()
包
包将有联系的模块组织在一起,放在同一个文件夹下,这个文件夹就称之为包。每个包下默认会有一个__init__.py
文件。(__init__.py
控制着包的导入行为)
__init__.py
文件定义了包的属性和方法。其实它可以什么也不定义;可以只是一个空文件,但是必须存在。如果__init__.py
不存在,这个目录就仅仅是一个目录,而不是一个包,它就不能被导入或者包含其它的模块和嵌套包。
导入包的三种方式:
-
import 包名[.模块名 [as 别名]]
import my_package.module1
# 必须通过包名.模块名的方式访问模块中的成员
my_package.module1.hello()
通过此语法格式导入包中的指定模块后,在使用该模块中的成员(变量、函数、类)时,需添加“包名.模块名”为前缀
别名方式:
import my_package.module1 as module1
# 别名方式可直接通过别名访问模块中的成员
module1.hello()
当然,如果使用 as 给包名.模块名起一个别名的话,就使用直接使用这个别名作为前缀使用该模块中的方法可使用这个别名作为前缀使用该模块中的方法。
-
from 包名 import 模块名 [as 别名]
# 导入my_package包下的module1模块
from my_package import module1
module1.hello()
使用此语法格式导入包中模块后,在使用其成员时不需要带包名前缀,但需要带模块名前缀。
-
from 包名.模块名 import 成员名 [as 别名]
此语法格式用于向程序中导入“包.模块”中的指定成员(变量、函数或类)。通过该方式导入的变量(函数、类),在使用时可以直接使用变量名(函数名、类名)调用:
# 导入my_package包下的module1模块中的hello成员
from my_package.module1 import hello
hello()
在使用此种语法格式加载指定包的指定模块时,可以使用 * 代替成员名,表示加载该模块下的所有成员。例如:
from my_package.module1 import *
只有以“from 模块名 import *”形式导入的模块,当该模块设有
__all__
变量时,只能导入该变量指定的成员,未指定的成员是无法导入的。
异常
程序运行时常会碰到一些错误,例如除数为 0、年龄为负数、数组下标越界等,这些错误如果不能发现并加以处理,很可能会导致程序崩溃。和 C++、Java 这些编程语言一样,Python 也提供了处理异常的机制,可以让我们捕获并处理这些错误,让程序继续沿着一条不会出错的路径执行。
异常处理
-
捕获指定异常类型
nums = []
nums[0]
try:
print(nums[0])
except IndexError:
print("异常了")
-
捕获多个指定异常
通过多 except 代码块,捕获不同类型的异常。
try:
print(1/0)
except IndexError:
print("下标越界")
except ZeroDivisionError:
print("除数不能为0")
except Exception:
print("未知错误")
同时,每个 except 块都可以同时处理多种异常。
try:
print(1/0)
except (IndexError,ZeroDivisionError):
print("下标越界或除数不能为0")
except Exception:
print("未知错误")
-
捕获所有异常
所有异常的父类都是Exception,通过Exception即可捕获所有异常信息。
try:
print(10+"")
except Exception:
print("报错了")
-
异常的捕获信息
针对未知异常信息,可以通过Exception as 变量名
方式捕获特定的异常信息。
try:
print(10+"")
except Exception as ex:
print(f"{ex}")
异常的else
在原本的try except
结构的基础上,Python异常处理机制还提供了一个 else 块,也就是原有 try except 语句的基础上再添加一个 else 块,即try except else
结构。
使用 else 包裹的代码,只有当 try 块没有捕获到任何异常时,才会得到执行;反之,如果 try 块捕获到异常,即便调用对应的 except 处理完异常,else 块中的代码也不会得到执行。
try:
print(str(10)+"10")
except Exception:
print("报错了")
else:
print("没有出现异常情况")
异常的finally
Python异常处理机制还提供了一个 finally 语句,通常用来为 try 块中的程序做扫尾清理工作。
在整个异常处理机制中,finally 语句的功能是:无论 try 块是否发生异常,最终都要进入 finally 语句,并执行其中的代码块。
try:
print(str(10)+"10")
except Exception:
print("报错了")
else:
print("执行else中的代码")
finally:
print("执行finally中的代码")
基于 finally 语句的这种特性,在某些情况下,当 try 块中的程序打开了一些物理资源(文件、数据库连接等)时,由于这些资源必须手动回收,而回收工作通常就放在 finally 块中。
自定义异常
创建自定义异常类:
class MyException(Exception):
def __init__(self, msg: str):
self.msg = msg
def __str__(self):
return self.msg
模拟自定义异常引发场景:
try:
a: int = 2
if a < 5:
raise MyException("a的值不能小于5")
except Exception as ex:
print(ex)
文件
和其它编程语言一样,Python 也具有操作文件(I/O)的能力,比如打开文件、读取和追加数据、插入和删除数据、关闭文件、删除文件等。
open()函数
在 Python 中,如果想要操作文件,首先需要创建或者打开指定的文件,并创建一个文件对象,而这些工作可以通过内置的 open() 函数实现。
open() 函数用于创建或打开指定文件,该函数的常用语法格式如下:
file = open(file_name [, mode='r' [ , buffering=-1 [ , encoding = None ]]])
此格式中,用 [] 括起来的部分为可选参数,即可以使用也可以省略。其中,各个参数所代表的含义如下:
-
file:表示要创建的文件对象。
-
file_name:要创建或打开文件的文件名称,该名称要用引号(单引号或双引号都可以)括起来。需要注意的是,如果要打开的文件和当前执行的代码文件位于同一目录,则直接写文件名即可;否则,此参数需要指定打开文件所在的完整路径。
-
mode:可选参数,用于指定文件的打开模式。如果不写,则默认以只读(r)模式打开文件。
-
buffering:可选参数,用于指定对文件做读写操作时,是否使用缓冲区(本节后续会详细介绍)。
-
encoding:手动设定打开文件时所使用的编码格式,不同平台的 ecoding 参数值也不同,以 Windows 为例,其默认为 cp936(实际上就是 GBK 编码)。
open() 函数支持的文件打开模式:
模式 | 说明 | 注意实现 |
r | 只读模式打开文件,读文件内容的指针会放在文件的开头。 | 操作的文件必须存在。 |
rb | 以二进制格式、采用只读模式打开文件,读文件内容的指针位于文件的开头,一般用于非文本文件,如图片文件、音频文件等。 | |
r+ | 打开文件后,既可以从头读取文件内容,也可以从开头向文件中写入新的内容,写入的新内容会覆盖文件中等长度的原有内容。 | |
rb+ | 以二进制格式、采用读写模式打开文件,读写文件的指针会放在文件的开头,通常针对非文本文件(如音频文件)。 | |
w | 以只写模式打开文件,若该文件存在,打开时会清空文件中原有的内容。 | 若文件存在,会清空其原有内容(覆盖文件);反之,则创建新文件。 |
wb | 以二进制格式、只写模式打开文件,一般用于非文本文件(如音频文件) | |
w+ | 打开文件后,会对原有内容进行清空,并对该文件有读写权限。 | |
wb+ | 以二进制格式、读写模式打开文件,一般用于非文本文件 | |
a | 以追加模式打开一个文件,对文件只有写入权限,如果文件已经存在,文件指针将放在文件的末尾(即新写入内容会位于已有内容之后);反之,则会创建新文件。 | |
ab | 以二进制格式打开文件,并采用追加模式,对文件只有写权限。如果该文件已存在,文件指针位于文件末尾(新写入文件会位于已有内容之后);反之,则创建新文件。 | |
a+ | 以读写模式打开文件;如果文件存在,文件指针放在文件的末尾(新写入文件会位于已有内容之后);反之,则创建新文件。 | |
ab+ | 以二进制模式打开文件,并采用追加模式,对文件具有读写权限,如果文件存在,则文件指针位于文件的末尾(新写入文件会位于已有内容之后);反之,则创建新文件。 |
文件读写
示例一:读
Python提供了如下 3 种函数,它们都可以帮我们实现读取文件中数据的操作:
函数 | 说明 |
---|---|
read() | 逐个字节或者字符读取文件中的内容 |
readline() | 逐行读取文件中的内容 |
readlines() | 一次性读取文件中多行内容 |
try:
# 1.读取整个文件,并返回列表
print(f.readlines())
# 2.读取一行
print(f.readline())
# 3.逐个字节或者字符读取文件中的内容
print(f.read(8))
except Exception as ex:
print(ex)
finally:
# 关闭文件
f.close()
示例二:写
函数 | 说明 |
---|---|
write() | 可以向文件中写入指定内容。注意:在使用 write() 向文件中写入数据,需保证使用 open() 函数是以 r+、w、w+、a 或 a+ 的模式打开文件,否则执行 write() 函数会抛出 io.UnsupportedOperation 错误。 |
writelines() | 可以实现将字符串列表写入文件中。注意:使用 writelines() 函数向文件中写入多行数据时,不会自动给各行添加换行符。 |
注意,写入函数只有 write() 和 writelines() 函数,而没有名为 writeline 的函数。
f = open("a.txt", "r+", encoding="utf-8")
# 不换行
f.write("hello world")
f.write("hello world")
f.write("hello world")
# 换行
f.write("hello world\n")
f.write("hello world\n")
f.write("hello world\n")
# 追加,这个需要打开模式为a
f.write("ni hao python")
实例三:with as
使用 with as 操作已经打开的文件对象(本身就是上下文管理器),无论期间是否抛出异常,都能保证 with as 语句执行完毕后自动关闭已经打开的文件。
with as 语句的基本语法格式为:
with 表达式 [as target]:
代码块
此格式中,用 [] 括起来的部分可以使用,也可以省略。其中,target 参数用于指定一个变量,该语句会将 expression 指定的结果保存到该变量中。with as 语句中的代码块如果不想执行任何语句,可以直接使用 pass 语句代替。
with open("a.txt", "w+", encoding="utf-8") as f:
# ☺占用4个字节
f.write("hello☺world")
f.write("hello☺world")
f.write("hello☺world")
GBK:中文每个字符占用2个字节,英文1个字节 UTF-8:中文每个字符占用3个字节,英文1个字节
高阶函数
高阶函数:一个函数可以作为参数传给另外一个函数,或者一个函数的返回值为另外一个函数(若返回值为该函数本身,则为递归),满足其一则为高阶函数。
filter()函数
filter(func,lst)
函数(又称之为过滤函数)用于过滤序列,过滤掉不符合条件的元素,返回一个filter对象,可将其转换成list。
nums = [1, 2, 3, 4, 5, 6, 7]
# 1.filter 过滤函数
f = filter(lambda a: a > 5, nums)
map()函数
map(func,lst)
函数(又称之为遍历函数)将传入的函数变量func作用到lst变量的每个元素中,并将结果组成新的列表(Python2)或者迭代器对象(Python3)。
nums = [1, 2, 3, 4, 5, 6, 7]
# 2.map 遍历函数
m = map(lambda a: a * 2, nums)
reduce()函数
reduce(func,lst)
函数(又称之为规约函数),其中func必须有两个参数,每次func计算的结果继续和序列的下一个元素做累计计算处理。
通过reduce()函数计算总和:
nums = [1, 2, 3, 4, 5, 6, 7]
r = reduce(lambda a, b: a + b, nums)
print(r)
初始状态a和b分别代表nums列表中的第一个元素和第二个元素,然后计算a+b得到结果,再将结果与列表中的第三个元素进行累计计算,以此类推。
通过reduce()函数计算列表中的奇数:
nums = [1, 2, 3, 4, 5, 6, 7]
def func_sj(a, b):
if b % 2 == 1:
a.append(b)
return a
r1 = reduce(func_sj, nums, [])
print(r1)