Python 模块和包

本文主要是学习了官方文档模块和包之后进行练习,得到的关于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.pyc.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.pycversion一般是 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里面有两个子包e1e2,子包中包含了各自的模块。模块里的代码很简单:

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.兄弟包的相互导入

① 使用绝对导入

e2module_3使用e1module_1

from e.e1 import module_1

module_1.func_1()
复制代码

修改e.py文件的内容如下:

from e.e2 import module_3
复制代码

在终端执行python3 e.py,会打印出module_1

使用绝对导入会比较清晰和方便。

② 使用相对导入

e1module_2中使用e1module_1

from . import module_1
复制代码

e2module_3使用e1module_1

from ..e1 import module_1
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享