学习目标:了解Python中模块和包的概念,掌握相关包和模块的导入和使用方法
模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。这也是使用 python 标准库的方法。
假设你写了一个10000行代码的程序,如果把全部代码写在一个文件里,查询起来将会非常困难。每次出现错误或异常时,不得不快速浏览10000行代码来查找导致问题的那行。为解决这个问题,程序员将大型程序分割成多个包含Python代码的文件,也就是被称为模块(Module)。Python支持在一个模块中使用另一个模块内的代码,也有内置模块,它是Python语言自带的,包含了许多重要的功能。本节将学习模块及其使用方式。
一、模块的概念
模块就是一个工具包,这个工具包(模块)对外提供全局变量、函数、类三种类型的工具,工具包名称(模块名)实则是一个标识符,需要满足标识符命名规范,而模块的本质就是以.py为扩展名的源代码文件。
模块的开发原则也很简单,就是每一个文件应该是可以被导入的,我们可以理解为:
模块中所有没有任何缩进的代码都应该被执行一遍,加载到内存,以供调用者使用;
模块中的测试代码应该保证仅在其内部使用,而被导入到其他文件中应该不被执行;
这么一看,那么问题来了,我们该如何让测试代码满足以上的执行条件呢?接下来我们需要学习一下python中的一个内置属性__name_,此属性记录着一个字符串,详情如下表:
| 分情况讨论 | __name__值 |
|---|---|
| 模块为当前执行程序时 | __main__(双下划线字符串) |
| 模块被其他文件导入时 | 被导入模块名 |
由此,我们可以好好使用这个属性,将我们的测试代码放入其中,如下:
if __name__ = "__main__":
# 此处写测试代码
pass
这样,我们的测试代码仅在模块为当前执行程序时,被导入将不再执行,大功告成!最后,给出推荐的模块代码格式,如下:
# 导入模块
# 定义全局变量
# 定义类
# 定义函数
def main():
"""
此处为测试代码,是否已解惑各位同学们心中那些年的疑惑呀?
"""
pass
if __name__ = "__main__":
"""
仅在模块为当前执行程序时,执行main();
被其他文件导入时,将不再执行main()
"""
main()
模块的作用总结起来有三点:
- 提高代码重用性
- 系统命名空间的划分
- 实现共享服务和数据
模块分为三种:
- 内置模块
- 第三方模块
- 自定义模块
模块导入规范
- 文件头部导入
- 内置模块放在最开始
- 第三方模块放在中间
- 自定义模块放在最后
- 不同种类模块之间空一行
二、包的概念
包是一种管理 Python 模块命名空间的形式,采用”点模块名称”。比如一个模块的名称是 A.B, 那么他表示一个包 A中的子模块 B 。就好像使用模块的时候,你不用担心不同模块之间的全局变量相互影响一样,采用点模块名称这种形式也不用担心不同库之间的模块重名的情况。这样不同的作者都可以提供 NumPy 模块,或者是 Python 图形库。不妨假设你想设计一套统一处理声音文件和数据的模块(或者称之为一个”包”)。现存很多种不同的音频文件格式(基本上都是通过后缀名区分的,例如: .wav,:file:.aiff,:file:.au,),所以你需要有一组不断增加的模块,用来在不同的格式之间转换。并且针对这些音频数据,还有很多不同的操作(比如混音,添加回声,增加均衡器功能,创建人造立体声效果),所以你还需要一组怎么也写不完的模块来处理这些操作。这里给出了一种可能的包结构(在分层的文件系统中):
sound/ 顶层包
__init__.py 初始化 sound 包
formats/ 文件格式转换子包
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ 声音效果子包
__init__.py
echo.py
surround.py
reverse.py
...
filters/ filters 子包
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
在导入一个包的时候,Python 会根据 sys.path 中的目录来寻找这个包中包含的子目录。
目录只有包含一个叫做 __init__.py 的文件才会被认作是一个包,主要是为了避免一些滥俗的名字(比如叫做 string)不小心的影响搜索路径中的有效模块。
最简单的情况,放一个空的 :file:__init__.py就可以了。当然这个文件中也可以包含一些初始化代码或者为(将在后面介绍的) __all__变量赋值。
用户可以每次只导入一个包里面的特定模块,比如:
import sound.effects.echo
这将会导入子模块:sound.effects.echo。 他必须使用全名去访问:
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
还有一种导入子模块的方法是:
from sound.effects import echo
这同样会导入子模块: echo,并且他不需要那些冗长的前缀,所以他可以这样使用:
echo.echofilter(input, output, delay=0.7, atten=4)
还有一种变化就是直接导入一个函数或者变量:
from sound.effects.echo import echofilter
同样的,这种方法会导入子模块: echo,并且可以直接使用他的 echofilter() 函数:
echofilter(input, output, delay=0.7, atten=4)
注意当使用 from package import item 这种形式的时候,对应的 item 既可以是包里面的子模块(子包),或者包里面定义的其他名称,比如函数,类或者变量。
import 语法会首先把 item 当作一个包定义的名称,如果没找到,再试图按照一个模块去导入。如果还没找到,抛出一个 :exc:ImportError 异常。
反之,如果使用形如 import item.subitem.subsubitem 这种导入形式,除了最后一项,都必须是包,而最后一项则可以是模块或者是包,但是不可以是类,函数或者变量的名字。
模块:一个包含所有你定义的函数和变量的文件,其后缀名是
.py,一个.py文件就是一个模块包:一个包含
__init__.py模块 的文件夹,一般也会包含其他一些模块和子包库(lib):库是完成一定功能的代码集合,具体表现可以是包,也可以是一个模块
框架(framework):为解决一个开放性问题而设计的具有一定约束性的支撑结构
python内置了一些库,除此之外,还有其他人自己做的一些库,称之为第三方库
一般把第三方库放在…/python3/lib/site_packages中
三、模块和包的导入
使用模块之前,必须先导入(import):意味着要写代码,以便让Python知道从哪获取模块。Python导入模块有以下几种方式:
import module
from module.xx.xx import xx 精确导入
from module.xx.xx import xx as rename
from module.xx.xx import * 模糊导入
导入示例如下:
import math
from random import randint
import pandas as pd
from tkinter import *
math.pow(2, 3) # 2的3次方
randint(1, 10) # 随机生成1-10的一个整数
导入模块其实就是告诉Python解释器去解释那个py文件
- 导入一个
py文件,解释器解释该py文件 - 导入一个包,解释器解释该包下的
__init__.py文件
模糊导入中的*中的模块是由__all__来定义的,__init__.py的另外一个作用就是定义package中的__all__,用来模糊导入,如__init__.py:
__init__.py 控制着包的导入行为。假如 __init__.py 为空,那么仅仅导入包是什么都做不了的。
from dingxiangyuan import * # 虽然dingxiangyuan文件夹下有DBHelper模块 但没有定义__all__
DBHelper
Traceback (most recent call last):
File "D:\python37\lib\site-packages\IPython\core\interactiveshell.py", line 3319, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-3-60abe7946ad7>", line 1, in <module>
DBHelper
NameError: name 'DBHelper' is not defined
定义变量all
__all__ = ['DBHelper']
包外调用
from dingxiangyuan import *
DBHelper
Out[3]: <module 'dingxiangyuan.DBHelper' from 'E:\\ZhangYafei\\project\\丁香园\\dingxiangyuan\\dingxiangyuan\\DBHelper.py'>
从上边的例子可以看出,init.py的主要作用是:
- Python中package的标识,不能删除
- 定义__all__用来模糊导入
- 编写Python代码(不建议在init__中写python模块,可以在包中在创建另外的模块来写,尽量保证__init.py简单)
那么问题来了,导入模块时是根据那个路径作为基准来进行的呢?即:sys.path
>>> import sys
>>> sys.path
['', 'F:\\python37\\python37.zip', 'F:\\python37\\DLLs', 'F:\\python37\\lib', 'F:\\python37', 'C:\\Users\\Administrator\\AppData\\Roaming\\Python\\Python37\\site-packages', 'C:\\Users\\Administrator\\AppData\\Roaming\\Python\\Python37\\site-packages\\win32', 'C:\\Users\\Administrator\\AppData\\Roaming\\Python\\Python37\\site-packages\\win32\\lib', 'C:\\Users\\Administrator\\AppData\\Roaming\\Python\\Python37\\site-packages\\Pythonwin', 'F:\\python37\\lib\\site-packages']
搜索规则:
- 搜索
当前目录,若模块名存在,则直接导入,否则继续搜索; - 搜索
系统目录,若模块名存在,则直接导入,否则调用模块中的方法时,将会抛出异常;
如果sys.path路径列表没有你想要的路径,可以通过 sys.path.append('路径') 添加。

例如:将当前文件的目录的上一层目录添加到环境路径中。
import os,sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
print(sys.path)

dir() 函数
内置的函数 dir() 可以找到模块内定义的所有名称。以一个字符串列表的形式返回:
>>> import os
>>> dir(os)
['DirEntry', 'F_OK', 'MutableMapping', 'O_APPEND', 'O_BINARY', 'O_CREAT', 'O_EXCL', 'O_NOINHERIT', 'O_RANDOM', 'O_RDONLY', 'O_RDWR', 'O_SEQUENTIAL', 'O_SHORT_LIVED', 'O_TEMPORARY', 'O_TEXT', 'O_TRUNC', 'O_WRONLY', 'P_DETACH', 'P_NOWAIT', 'P_NOWAITO', 'P_OVERLAY', 'P_WAIT', 'PathLike', 'R_OK', 'SEEK_CUR', 'SEEK_END', 'SEEK_SET', 'TMP_MAX', 'W_OK', 'X_OK', '_Environ', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_check_methods', '_execvpe', '_exists', '_exit', '_fspath', '_get_exports_list', '_putenv', '_unsetenv', '_wrap_close', 'abc', 'abort', 'access', 'altsep', 'chdir', 'chmod', 'close', 'closerange', 'cpu_count', 'curdir', 'defpath', 'device_encoding', 'devnull', 'dup', 'dup2', 'environ', 'error', 'execl', 'execle', 'execlp', 'execlpe', 'execv', 'execve', 'execvp', 'execvpe', 'extsep', 'fdopen', 'fsdecode', 'fsencode', 'fspath', 'fstat', 'fsync', 'ftruncate', 'get_exec_path', 'get_handle_inheritable', 'get_inheritable', 'get_terminal_size', 'getcwd', 'getcwdb', 'getenv', 'getlogin', 'getpid', 'getppid', 'isatty', 'kill', 'linesep', 'link', 'listdir', 'lseek', 'lstat', 'makedirs', 'mkdir', 'name', 'open', 'pardir', 'path', 'pathsep', 'pipe', 'popen', 'putenv', 'read', 'readlink', 'remove', 'removedirs', 'rename', 'renames', 'replace', 'rmdir', 'scandir', 'sep', 'set_handle_inheritable', 'set_inheritable', 'spawnl', 'spawnle', 'spawnv', 'spawnve', 'st', 'startfile', 'stat', 'stat_result', 'statvfs_result', 'strerror', 'supports_bytes_environ', 'supports_dir_fd', 'supports_effective_ids', 'supports_fd', 'supports_follow_symlinks', 'symlink', 'sys', 'system', 'terminal_size', 'times', 'times_result', 'truncate', 'umask', 'uname_result', 'unlink', 'urandom', 'utime', 'waitpid', 'walk', 'write']
>>>
如果没有给定参数,那么 dir() 函数会罗列出当前定义的所有名称:
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'os', 'randint', 'sys']
>>> del os
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'randint', 'sys']
>>>
四、第三方模块
安装第三方库实质上是下载并使用别人写的代码。常见的第三方库的格式:源码(Source)(压缩包,需先解压,得到包含setup.py文件夹);egg(本质是压缩包);whl(本质也是压缩文件)。
1. 下载和安装
源码安装(本地安装):手动下载,再安装到本地;
(1)到对应库托管网站下载所需要的文件,https://pypi.org/或https://www.lfd.uci.edu/~gohlke/pythonlibs/等
(2)下载的文件要对应自己python的版本,还要选择32位还是64位安装的python的系统
(3)打开命令行(win+R cmd):切换到下载的文件所在的目录 cd /d
(4)对于带setup.py的文件(源码文件),输入命令:python setup.py install。注意:若没有
setuptools包,需手动下载安装,再利用setuptools安装其他使用setuptools打包的包(5)对于 .egg文件使用easy_stall安装,输入命令:easy_install xxx.egg(完整文件名称)
(6)对于
whl文件可以使用easy_stall如上安装,也可以使用pip安装,输入命令:pip install xxx.whl(完整文件名称)包管理安装(远程安装):通过命令使自动化的为用户安装管理包和模块。安装命令如下:
yum(linux),pip,apt-get(linux)使用国内源安装
默认pip是使用Python官方的源,但是由于国外官方源经常被墙,导致不可用,我们可以使用国内的python镜像源,从而解决Python安装不上库的烦恼。国内有很多源,比如:
(1)阿里云 http://mirrors.aliyun.com/pypi/simple/ (2)豆瓣http://pypi.douban.com/simple/ (3)清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/ (4)中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple/ (5)华中科技大学http://pypi.hustunique.com/临时使用
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 模块名永久修改,一劳永逸
Linux下,修改 ~/.pip/pip.conf (没有就创建一个), 修改 index-url至tuna,内容如下: [global] index-url = http://mirrors.aliyun.com/pypi/simple/ [install] trusted-host=mirrors.aliyun.com windows下,直接在user目录中创建一个pip目录,如:C:\Users\xx\pip,新建文件pip.ini,内容如下: [global] index-url = http://mirrors.aliyun.com/pypi/simple/ [install] trusted-host=mirrors.aliyun.com
2. 卸载
pip uninstall 模块名
3. 更新pip
若提示更新pip
WARNING: You are using pip version 20.2.3; however, version 20.2.4 is available.
You should consider upgrading via the 'f:\python37\python.exe -m pip install --upgrade pip' command.
按提示输入 python -m pip install –upgrade pip,还是报错
ValueError: Unable to find resource t64.exe in package pip._vendor.distlib
WARNING: You are using pip version 20.2.3; however, version 20.2.4 is available.
You should consider upgrading via the 'F:\python37\python.exe -m pip install --upgrade pip' command
解决方法
python -m pip uninstall pip setuptools
pip3 install --upgrade pip
五、内置模块
官网网址:https://docs.python.org/3/py-modindex.html
关于内置模块,也称标准库模块,我们将再另一章节讲解。