模块和包
模块和包
1,认识模块
为了编写可维护的代码,需要把很多个类和函数进行分组,分门别类地放到不同的文件里这样每个文件的代码就相对减少了,维护起来也变得轻松。
在python中,一个以.py为扩展名的文件就叫做一个模块(Module),每一个模块在Python里都是一个独立的文件。模块可以包含直接运行的代码、类定义、函数定义等任何Python源代码。模块可以被其他模块、脚本、甚至是交互解析器导入(import)使用,也可以被其他程序引用。导入的源代码会直接被解析运行。
使用模块的好处:
1 | #提高代码的可维护性 |
Python模块可以分为3种类型
1 | #内置标准模块,又称为标准库,如sys、time、json模块等 |
【提示】:
1 | Python模块一般都位于安装目录下Lib文件中,执行help("modules")命令,可以查看已经安装的所有模块列表。 |
1 | 自定义模块的名称不能与系统模块重名,否则有覆盖掉内置模块的风险。例如:自定义一个sys.py模块后,就不能再使用系统的sys模块。 |
2,使用模块
Python内置了很多非常有用的模块,只要成功安装Pyhthon,就可以使用。
1,导入模块
使用import语句导入模块,语法格式如下:
1 | import module1[,module2[...,moduleN]] |
import 关键字后面是一组模块的列表,多个模块之间使用逗号分隔。module1、module2和moduleN等表示模块名称,即Python文件名,模块名称不包括.py扩展名。
当python解释器在源代码中解析到import关键字时,会自动在搜索路径中搜寻对应的模块,如果发现就会立即导入。
【提示】
1 | 搜索路径是一个目录列表,供Python解释器在导入模块时进行参考,可以事先配置,或者在源代码中设置。 |
【示例1】本示例用import语句从Python标准库中导入sys模块
1 | import sys |
sys是Python的内置模块,当执行import sys命令后,Python在sys.path变量所列目录中寻找sys模块文件的路径。导入成功,会运行这个模块的源代码并进行初始化,然后就可以使用该模块了。
导入sys模块之后,可以使用dir(sys)方法查看该模块中可用的成员。sys.modules是一个字典对象,每当导入新的模块,sys.modules会自动记录该模块。
【提示】:
1 | 一般建议import命令放在脚本文档的顶端。在一个文档中,不管执行了多少钱次import命令,一个模块仅导入一次,这样可以防止滥用import命令。 |
访问模块中的变量,函数、类对象时,可以使用点语法,在变量名,函数名和类名前面添加模块前缀,如sys.modules、sys.argv(命令),sys.path(搜索路径列表)等。
如果模块名比较长,在导入模块时可以给他另起一个别名,语法格式如下:
1 | import modules as 模块名 |
在导入模块语句的后面添加as关键字,设置一个简单、好记的别名。然后,在脚本中就可以使用别名来访问模块内的变量、函数和类等对象。
【示例2】本示例使用import命令导入random模块,设置一个别名r,然后就可以使用r访问该模块中的randint()函数,随机生成10个0-10之间的随机数。
1 | import random as r |
2,搜索路径
搜索路径类似环境变量,它由一系列目录名组成,Python解释器能够依次从这些目录中去寻找所要的导入的模块。
搜索路径是在Python编译或安装的时候确定的,安装新的库应该也会修改。搜索路径被存储在sys模块的path变量中。例如,在交互式命令行中输入;
1 | import sys |
输出为:
1 | ['', 'D:\\python\\python310.zip', 'D:\\python\\DLLs', 'D:\\python\\lib', 'D:\\python', 'D:\\python\\lib\\site-packages'] |
sys.path输出一个列表,如果第一项是空字符串’ ‘,则表示当前目录,即执行python解释器的目录,对于脚本来说就是运行脚本所在的目录。如果第一项不是空字符串,则它也表示完整的当前目录。
一般python会按如下顺序搜索模块。
1 | 第1步,当前目录。 |
手动添加搜索路径有3种方法,具体说明如下:
1,临时添加
可以直接在脚本中为sys.path列表添加目录项。例如:
1 | import sys |
1 | ['', 'D:\\python\\python310.zip', 'D:\\python\\DLLs', 'D:\\python\\lib', 'D:\\python', 'D:\\python\\lib\\site-packages','E:/path/'] |
这样就可以在列表的尾部看到新添加的目录。
2,添加.pth文件
.pth类型的文件是Python专用的目录列表文件,把该类型的文件置于Python安装目录下Lib/site-packages文件夹中,Python编译起就会自动搜索,并添加其中的目录。
【示例】通过一个简单实例演示如何通过添加和使用.pth文件
1 | 第1步,新建文本文件,保存为test.pth,文件名可以任意,然后在其中输入下面目录。(E:/test) |
【提示】
1 | 创建.pth文件后,应该重新打开测试文件,重新导入模块的Python文件,否则添加的目录可能会找不到。新建模块文件时,文件名不要与搜索路径下的文件出现重名,否则会被覆盖。 |
3,添加环境变量
【提示】
1 | 使用环境变量添加搜索路径,可以适用不同的版本Python,而.pth类型的文件仅被Python3.0新版本支持。 |
3,导入成员
使用import导入模块时,Python都会创建一个新的命名空间,并在该命名空间中执行.py文件的所有代码,同时访问模块中
的变量,函数或类名时,都需要添加模块名前缀。
可以使用from…import语句将模块中具体的函数、类或变量等成员导入当前命名空间中直接使用,语法格式如下:
1 | from 模块 import 成员 |
模块成员包括变量、函数或者类等,可以同时导入多个成员,多个成员之间使用逗号进行分隔,如果想要导入全部成员,可以使用通配符代替,例如:
1 | from 模块 import * |
【提示】
1 | 当导入模块中所有成员之后,可以使用print(dir())函数查看导入的所有成员: |
1 | ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'altzone', 'asctime', 'ctime', 'daylight', 'get_clock_info', 'gmtime', 'localtime', 'mktime', 'monotonic', 'monotonic_ns', 'perf_counter', 'perf_counter_ns', 'process_time', 'process_time_ns', |
【注意】
使用from…import 语句时,要确保当前命名的空间内不存在与导入名称一致的内容,否则将会引发冲突。最后导入的同名变量、函数或者类名将会覆盖前面的内容。因此,如果无法确定这种风险,则建议使用import语句直接导入模块,而不是导入模块成员。
4,使用标准模块
Python内置了很多标准模块,涵盖了核心功能、线程和进程、数据表示、文件格式、邮件和新闻消息处理、网络协议、国际化、多媒体相关模块、数据存储、工具和实用程序、其他模块等。其中核心功能又包括运行时服务、文字模式匹配、操作系统接口、数学运算、对象永久保存、网络和GUI等方面。其中常用模块说明如下所示:
【示例】Python标准模块众多,下面结合JSON模块简单演示,了解如何使用它们
第1步,使用import语句JSON模块,先使用dir()函数查看该模块包含的所有函数和属性。
1 | import json |
输出为:
1 | ['JSONDecodeError', 'JSONDecoder', 'JSONEncoder', '__all__', '__author__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', '_default_decoder', '_default_encoder', 'codecs', 'decoder', 'detect_encoding', 'dump', 'dumps', 'encoder', 'load', 'loads', 'scanner'] |
第2步,使用load()函数解码JSON数据,该函数返回Python字段的数据类型。可以使用下面命令查看该函数的基本用法:
1 | print(help(json.loads)) |
第3步,把JSON字符串转换为Python字典对象
1 | import json |
输出为:
1 | {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5} |
5,使用第三方模块
除了使用Python内置的标准模块外,也可以使用第三方模块。访问http://pypi.python.org/pypi,可以查看Python开源模块库。收录了许多来自全世界Python开发者贡献的模块,机会涵盖了想用Python做的任何事情。
【提示】
1 | 也可以访问https://www.ifd.uci.edu/~gohlke/pythonlibs/,下载Python扩展包的windows二进制文件。 |
使用第三方模块时,首先需要下载并安装该模块,然后就可以像使用标准模块一样导入并使用。下面介绍下载和安装模块,具体方法有两种:
1,下载模块并安装
在PyPI首页搜索模块,找到需要的模块后,单击Download files进入下载页面,然后可以选择下载二进制安装文件(.whl),或者源代码压缩包(.gz).
1 | #对于二进制文件来说,可以参观下面的安装方法,使用pip命令进行安装,安装时把模块名替换成为二进制安装文件即可。注意,在命令行下要改变当前目录到安装目录下。 |
2,使用pip命令安装
直接通过Python提供的pip命令安装。pip命令安装格式如下:
1 | pip install 模块名 |
pip命令会自动下载模块包并完成安装。pip命令会默认连接安装在国外的Python官方服务器进行下载。
【提示】
1 | 使用下面的命令可以卸载指定模块。 |
6,模块兼容性
在导入模块时,可以使用别名来兼容不同的版本或模块差异,这样在运行时可以根据当前环境选择最合适的模块。
【示例】在python2.6之前,simplejson是独立的第三方库,从python2.6开始被内置,如果需要兼容其他版本,可以使用下面的写法进行兼容。
1 | try: |
这样就可以优先使用json,如果用户使用的是老版本python,就可以降级使用simplejson。当导入simplejson时,使用as指定别名为json,确保后续代码引用json都可以正常工作。由于Python是动态语言,只要在脚本中保持相同的函数接口和用法,无论导入哪个模块,后续代码都能正常工作,不需要大范围修改代码。
7,自定义模块
自定义模块的一般步骤如下。
第1步,新建Python文件,文件命名格式如下:
1 | 模块名 + .py |
文件名及模块名,因此文件名不能够与Python内置模块重名。该文件名必须符合标识符规范。
第2步,在该文件中编写Python的源码,可以是变量、函数、类等功能代码。
第3步,把Python文件至于搜索路径中,如当前目录下等。
第4步,在脚本中使用import语句导入模块,然后就可以使用模块代码了。
【示例】演示一个简单的自定义模块设计过程
新建test1.py模块文件,然后输入下面的代码。
1 | #!/user/bin/env python3 |
在Python交互环境中导入test1模块,然后再调用模板中的函数saying()
1 | import test1.py |
在交互环境中导入test1模板之后,没有直接打印”Hello,world!”,因为name==”main“为False,无法执行条件语句中的saying()代码。只有调用test1.saying()函数时,才会打印”hello,world”。
3,使用包
包是一个有层次的文件目录结构,它定义了有多个模块或多个子包组成的Python应用程序执行环境。使用包可以组织和规范代码,避免模块重名问题。
1,认识包
当使用的模块文件越来越多时,就需要对模块文件进行规划。
同时为了避免模块命名冲突,Python引入了按目录来组织模块的方法,称为包(Package)。一个文件夹就是一个包,包可以相互嵌套,就像文件目录一样,包的名字就是文件家的名字,包名通常全部小写,避免使用下划线。
在包目录中,init.py用于标识当前文件夹是一个包,在Python2中,包就是文件夹,但该文件夹下必须包含init.py,该文件的内容可以为空,init.py用于标识当前文件夹时一个包。在Python3中,即使目录下没有包含init.py文件,也能创建包,不过还是建议创建包时加上init.py
2,创建包
创建包实际上就是创建文件夹,同时在该文件夹中建立一个名为init.py的python文件。init.py文件可以为空,也可以编写任意python代码,在导入包时将自动执行init.py文件包含的代码。
【示例】在当前工作目录中,新建ecommerce(电子商务)文件夹,设计为一个应用项目,同时新建main.py文件,作为项目的启动程序。在ecommerce包里再添加一个payment文件夹,新建嵌套的字包,用来管理不同的付款方式,文件夹的层次结构如下所示:
1 | parent_directory/ |
其中,products.py文件定义了Product类。
1 | class Product: |
其中,database.py文件定义了database类。
1 | class database: |
3,导入包
创建包之后,就可以在包中创建模块,然后再使用import语句从包中加载模块。模块的导入方法有两种:绝对路径导入和相对路径导入。
1,绝对路径导入
通过指定包、模块、成员的完整路径进行导入。
【示例1】以上一节的实例为例,在main.py中访问products模块中的Product类,可以使用如下方法之一进行导入。
1 | #方法一 |
import 语句使用点号作为分隔符分割包、模块、成员。
【注意】
1 | import语句只能导入模块或成员,不要使用import语句导入包,因为在脚本中不可以通过包访问模块,包对象只包括特殊的内置成员,如__doc__、__file__、__loader__、__name__、__package__、__package__、__path__、__spec__。 |
【提示】
1 | 如果模块中包含很多成员,建议使用第1种或第3种方式导入模块;如果模块中仅包含很少的成员,或者仅需要模块中个别成员,则可以考虑使用第2种方法导入具体的成员。 |
2,相对路径导入
在包(Package)中如果知道父模块的名称,那么就可以使用相对路径导入。具体方法有两种:
1 | # . 在导入路径前面添加1个点号,表示当前目录。 |
【示例2】以上一节的示例项目为例,当前在products模块中工作,想从相邻的database模块导入Database类,就可以使用下面相对路径导入。
1 | from .database import Database |
如果当前在commerce.payments.paypal模块中工作,需要引用父包中的database模块,就可以使用下面的相对路径导入:
1 | from ..databse import Database |
如果在commerce中新定义一个contact包,该包中新建email模块,需要将email模块的sendEmail函数导入到paypal模块中,则导入方法如下:
1 | from ..contact.email import sendEmail |
【注意】
1 | 使用相对路径导入模块或成员时,都必须从项目入口程序开始执行,不能够把模块作为脚本直接执行,否则Python解释器将以当前模块作为入口程序,当前目录作为工作目录,就无法理解整个项目的目录层次关系。因此,如果不以一个完整的项目进行运行,就不要使用相对路径导入内容。 |
4,案例实战
本节将结合实例介绍常用的内置模块的简单应用。
1,使用日期和时间模块
在开发中经常需要处理日期和时间,转换日期格式等操作。Python提供了time,datatime和calender这三个与时间相关的模块。熟悉这些标准模块的基本功能和使用,就能满足日常的开发需求。
1,time模块
在time模块中,通常有3种时间表达式
时间戳
时间戳表示从1970年1月1日00:00:00点开始按秒计算时间的偏移量,以浮点型表示。可以使用time模块的time()或者clock()等函数获取。例如:
1 | import time |
1 | 1699063787.2477338 |
【提示】
时间戳适合做日期运算,但是对于1970年之前或者太久之前的时间就无法表示了,UNIX和windows只支持到2038年。
格式化的自符串
可以根据需要选取各种日期、时间显示格式,其中最简单的方法是使用localtime()和asctime()函数。
例如:
1 | import time |
在上面的代码中,首先使用localtime()函数获取本地当前时间,返回为元组对象,然后通过asctime()函数进行格式化显示。
1 | Sat Nov 4 10:10:16 2023 |
也可以使用time模块中的strftime()函数来格式化日期:
1 | import time |
输出为:
1 | 2023-11-04 10:10:35 |
时间元组
很多函数使用一个元组包含9个数字来处理时间,者9个元素分别为年(tm_year),月(tm_mon),日(tm_mday),时(tm_hour),分(tm_min),秒(tm_sec),一周中的第几天(tm_wday),一年中的第几天(tm_yday),是否为夏时令(tm_isdst)。具体说明如下:
例如,下面使用localtime()函数获取当前时间的元组
1 | import time |
输出为:
1 | time.struct_time(tm_year=2023, tm_mon=11, tm_mday=4, tm_hour=10, tm_min=11, tm_sec=6, tm_wday=5, tm_yday=308, tm_isdst=0) |
2,datetime模块
datetime模块重新封装了time模块,提供更多接口,包含6个类,简单说明如下:
1 | #date:日期对象,常用属性有year、month、day |
【示例1】使用datetime模块中date类的now()函数获取当前时间,然后分别使用date(),time(),today()函数分别获取日期、时间和日期格式信息。
1 | import datetime |
输出结果:
1 | 2023-11-04 10:38:03.365221 |
【示例2】获取时间差。本例使用now()函数获取当前时间,然后计算一个for循环执行10万次所花费的时间,单位为毫秒。
1 | import datetime |
运行结果:
1 | 18986 |
差值不只是可以查看相差多少秒,还可以查看天(days)、秒(seconds)和微秒(microseconds)。
【示例3】计算当前时间向后8个小时的时间。
1 | import datetime |
运算结果:
1 | 2023-11-04 18:59:38.746353 |
timedelta类用来计算两个datetime对象的差值,构造函数的语法格式如下:
1 | datetime.timedelta(day=0,hours=0,days=0,weeks=0) |
其中参数都可选,默认值为0.使用这种方法可以计算:天(days)、小时(hours)、分钟(minutes)、秒(seconds)、微秒(microseconds)。
3,calender模块
calender模块用来处理年历和月历。例如:
1 | import calendar |
输出为:
1 | July 2020 |
2,使用随机数模块
random模块主要用于生成随机数。该模块提供的常用函数说明如下:
1 | #random.random():用于生成一个0~1.0范围内的随机浮点数(0<=n<1.0) |
【示例1】使用random模块随机生成各种类型的数据。
1 | import random |
运算结果:
1 | 0.16024680687171267 |
【示例2】使用random模块生成一个4位验证码。
1 | import random |
运行结果:
1 | A24S |
3,使用加密模块
Python的hashlib模块用来进行hash或者md5加密,这种加密是不可逆的,也称为摘要算法。该模块支持Openssl库提供的所有算法,包括MD5,sha1、sha224、sha256、sha512等。
该模块常用属性和方法说明如下:
1 | #algorithms_available:列出所有可用的加密算法,如(MD5,sha1、sha224、sha256、sha384、sha512) |
【示例】本示例是一个简单的加密示例
1 | import hashlib |
运算结果
1 | md5加密结果 a7f5f35426b927411fc9231b56382173 |
4,使用JSON模块
JSON(JavaScipt Object Notation)是一种轻量级的数据交换格式,JSON模块提供了4个方法用来实现Python对象与JSON数据进行快速交换,简单说明如下:
1 | #dumps():将Python对象序列化为JSON字符串表示。 |
【示例1】将字典对象序列化为JSON字符串,然后再反序列化为Python的字典类型的对象
1 | import json |
运行结果:
1 | {"name": "Tom", "age": 23} |
【示例2】下面示例设计将字典对象序列化为字符串,然后使用dump()方法保存到test.json文件中,再使用load()方法从test.json文件中读取字符串,并转换为字典对象。
1 | import json |
运行结果:
1 | {'name': 'Tom', 'age': 23} |
Python对象与JSON对象相互转换表
1 | Python对象:JSON对象 |
5,使用图像模块
PIL(Python Imaging Library)是Python图像处理标准库,功能强大,简单易用。PIL仅支持到Python2.7,升级后的版本改为Pillow,支持Python3版本,并增加了很多新特性。
1,安装Pillow
在命令行下通过pip命令安装。
1 | pip install pillow |
2,操作图像
在PIL中,图像的常用属性和方法简单概述如下。
图像的基本操作
1 | Image.open(file[,mode]):打开图像,生成Image对象。file表示要打开的图像文件,mode表示图像模式。 |
图像的基本属性
1 | image.format:图像来源,如果图像不是从文件读取,则值为None |
图像变换操作
1 | image.resize((width,height)):改变图像大小 |
图像合成、裁切和分离
1 | image.blend(img1,img2,alpha):合成两张图片,alpha表示img1和img2的比例 |
图像的像素点操作:
1 | image.point(function):对图片中的每一个点执行function函数。 |
【示例1】先打开一个图像文件,然后获取其尺寸,再缩小图像大小,最后命名为thumbnail.jpg保存到当前目录下。
1 | from Pillow import image |
【示例2】打开一个图像,然后旋转90°,之后再显示出来
1 | from PIL import Image |
【注意】
【示例3】在PIL的ImageDraw模块提供了一系列绘图方法,可以直接绘图。本示例利用该模块的方法生成字母验证码图片,将其命名为code.jpg,然后保存到当前目录。
1 | #第1步,从PIL模块中导入图像类、绘图类、图像字体类和图像特效类。 |