本文主要是学习了官方文档模块和包之后进行练习,得到的关于Python模块和包的笔记。
模块
一个模块(module)是包含Python定义和语句的文件。
文件名是模块名加上.py
后缀。a.py
文件就是名为a
的模块。在模块内部可以通过全局 __name__
拿到模块名称。
创建一个a.py
文件,简单地打印出模块名:
print(__name__)
复制代码
使用模块
可以在脚本或者解释器的交互实例中使用模块。当模块被导入的时候,会执行模块中的代码。
(1)在解释器交互实例中使用
在终端输入python3
进入解释器的交互模式,输入import a
:
>>> import a
a
复制代码
(2)以脚本的方式运行
在终端输入:
python3 module_name.py <arguments>
复制代码
这里使用python3
而不是python
是为了和python2
做区分。
当执行这句的时候,模块中的代码就会像import
时那样执行,但是 __name__
变量会被设置成'__main__'
。
可选的传递给命令行的参数列表<arguments>
可以通过sys
模块的sys.argv属性拿到。argv[0]
拿到的是脚本名称(如果使用-c <command>
执行Python代码,那么argv[0]
拿到的是'-c'
)。
修改a.py
代码如下:
print(__name__)
if __name__ == '__main__':
import sys
for argv in sys.argv:
print(argv)
复制代码
sys
是内置在解释器中的内置模块(也称为标准模块)之一。内置模块不是Python语言的核心,内置在解释器中是为了提高效率或者使用底层的操作系统。
执行python3 a.py 1 2 3
:
$ python3 a.py 1 2 3
__main__
a.py
1
2
3
复制代码
模块的私有符号表
每个模块都有自己的私有符号表,模块的私有符号表作为模块中定义的函数的全局符号表使用。在模块中使用全局变量不用担心与用户定义的全局变量发生冲突。可以使用module_name.item_name
的访问模块的全局变量。
创建两个文件b.py
,c.py
,在b.py
中用global定义全局变量global_var
,在c.py
中定义同名的全局变量:
b.py
:
global global_var
global_var = 'global_var_b'
复制代码
c.py
:
import b
global global_var
global_var = 'global_var_c'
print(b.global_var)
print(global_var)
复制代码
在这个例子中不明确使用global
声明全局变量也可以,因为在该位置声明的变量本身就是全局变量。
执行python c.py
,打印出:
global_var_b
global_var_c
复制代码
在模块b
中和模块c
中声明了相同名称的全局变量global_var
,但是因为它们分别在各自的私有符号表中,所以不会相互影响,造成冲突。
import模块的几种方式
创建模块d.py
:
print('ddd')
_d1 = 'd1'
def func_d1():
print(_d1)
def _func_d2():
print('d2')
复制代码
一般以_
开头的都是私有变量,只在模块内使用的,这里为了练习,还是在别的模块使用了_
开头的变量。
(1)直接导入模块
>>> import d
ddd
>>> d._d1
'd1'
>>> d.func_d1()
d1
>>> d._func_d2()
d2
复制代码
(2)导入所需的项
>>> from d import _d1, func_d1
ddd
>>> _d1
'd1'
>>> func_d1()
d1
复制代码
(3)导入所有非私有项
这是不推荐的方法,因为使用*
不能明确地知道导入了什么项。
>>> from d import *
ddd
>>> _d1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name '_d1' is not defined
>>> func_d1()
d1
复制代码
(4)设置别名
设置模块的别名:
>>> import d as d123
ddd
>>> d123.func_d1()
d1
复制代码
设置导入的项的别名:
>>> from d import func_d1 as func_d123
ddd
>>> func_d123()
d1
复制代码
模块搜索路径
上面的例子中导入模块的时候,不是使用import ./d.py
,而是直接用的import d
,找模块文件这件事由解释器为我们完成。
导入d
模块的时候,解释器的搜索路径如下:
(1)先查找名为d
的内置模块。
(2)没找到的话,再从 sys.path
.变量中的目录列表里查找 d.py
文件。
sys.path
中包含的的位置为:输入脚本的目录(或未指定文件时候的当前目录)、PYTHONPATH
环境变量的目录列表、默认安装目录。
在e.py
中添加:
import sys
print(sys.path)
复制代码
执行python3 e.py
打印出如下内容:
['/Users/momo/练习代码e.py所在的文件目录/code',
'/usr/local/Cellar/python@3.7/3.7.10_2/Frameworks/Python.framework/Versions/3.7/lib/python37.zip',
'/usr/local/Cellar/python@3.7/3.7.10_2/Frameworks/Python.framework/Versions/3.7/lib/python3.7',
'/usr/local/Cellar/python@3.7/3.7.10_2/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload',
'/Users/momo/virtualenvs/for_test/lib/python3.7/site-packages']
复制代码
模块缓存
为了提高加载模版的速度,Python会把模块的编译后版本缓存在 __pycache__
目录中,文件名为 module.version.pyc
,version
一般是 Python 的版本号。
经过上面的练习后的文件目录是这样的:
.
├── __pycache__
│ ├── a.cpython-37.pyc
│ ├── b.cpython-37.pyc
│ ├── c.cpython-37.pyc
│ └── d.cpython-37.pyc
├── a.py
├── b.py
├── c.py
└── d.py
复制代码
包
包是一种用“点式模块名”构造 Python 模块命名空间的方法。例如,模块名 A.B 表示包 A 中名为 B 的子模块。
我理解包(package
)就是一些整理起来的模块和__init__.py
文件组成的文件夹。可以通过A.B
这样的方式来获取。
创建包
Python把包含__init__.py
文件的目录当成包。__init__.py
可以是一个空文件,也可以包含包的舒适化代码,或者设置__all__
变量(这个变量用来声明当使用 from package import *
时导入的模块,注意import *
是不推荐的用法)。
我们创建一个包e
:
├── e
│ ├── __init__.py
│ ├── e1
│ │ ├── __init__.py
│ │ ├── module_1.py
│ │ └── module_2.py
│ └── e2
│ ├── __init__.py
│ └── module_3.py
复制代码
包e
里面有两个子包e1
和e2
,子包中包含了各自的模块。模块里的代码很简单:
var_1 = 'var_1'
def func_1():
print('module_1')
复制代码
使用包
创建一个文件e.py
来引入包进行练习:
├── e
│ ├── __init__.py
│ ├── ...
└── e.py
复制代码
在e.py
文件中使用包有以下两种方式:
1. 使用import
import e.e1.module_1
e.e1.module_1.func_1()
复制代码
这种方式每次使用的时候都需要写完整子模块名:e.e1.module_1
。
可以在终端执行python3 e.py
查看执行效果:
$ python3 e.py
module_1
复制代码
使用import item.subitem.subsubitem
的时候,除了最后一项(可以是包或模块)外,每个item
都必须是包。
import e.e1.module_1.func_1
ModuleNotFoundError: No module named 'e.e1.module_1.func_1'; 'e.e1.module_1' is not a package
复制代码
2.使用from ... import ...
(这是推荐用法)
from e.e1 import module_1
module_1.func_1()
复制代码
还可以直接导入所需的函数或者变量:
from e.e1.module_1 import func_1
func_1()
复制代码
使用from package import item
的时候,item
可以是包的子模块,也可以是包中定义的函数或者变量等。
3.兄弟包的相互导入
① 使用绝对导入
在e2
的module_3
使用e1
的module_1
:
from e.e1 import module_1
module_1.func_1()
复制代码
修改e.py
文件的内容如下:
from e.e2 import module_3
复制代码
在终端执行python3 e.py
,会打印出module_1
。
使用绝对导入会比较清晰和方便。
② 使用相对导入
在e1
的module_2
中使用e1
的module_1
:
from . import module_1
复制代码
在e2
的module_3
使用e1
的module_1
:
from ..e1 import module_1
复制代码