模块和包
python 的模块都是天然的单例
模块
import 加载的模块分为四个通用类别:
- 使用 python 编写的代码(.py 文件)
- 已被编译为共享库或 DLL 的 C 或 C++扩展
- 包好一组模块的包
- 使用 C 编写并链接到 python 解释器的内置模块
我们可以从sys.modules
中找到当前已经加载的模块,sys.modules
是一个字典,内部包含模块名与模块对象的映射,
该字典决定了导入模块时是否需要重新导入。
示例
自定义模块文件名my_module.py
,模块名my_module
# my_module.py
print("from the my_module.py")
money = 1000
def read1():
print("my_module->read1->money", money)
def read2():
print("my_module->read2 calling read1")
read1()
def change():
global money
money = 0
每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,
这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突
同时模块也是单例类,多次导入,'from the my_module.py'也只被 print 一次。
# 测试:执行my_module.change()操作的全局变量money仍然是my_module中的
# demo.py
import my_module
money = 1
my_module.change() # 改变的是my_module.money
print(money)
"""
执行结果:
from the my_module.py
1
"""
在一行导入多个模块
import sys, os, re
别名
import my_module as sm
from ... import...
from my_module import read1, read2
my_module.read1() # 可用
read1() # 可用
如果当前命名空间有重名 read1 或者 read2,那么会有覆盖效果。
别名
from my_module import read1 as read
全部导入
from my_module import * # 将模块my_module中所有的名字都导入到当前名称空间
此时需要在在my_module.py
中新增一行
__all__ = ["money", "read1"]
这样在另外一个文件中用from my_module import *
就这能导入列表中规定的两个名字
模块的加载与修改
每个模块相当于单例类,导入一次立被实例化,放入字典sys.modules
中了。
python 不支持重新加载或卸载之前导入的模块
如果只是你想交互测试的一个模块,使用importlib.reload(modulename)
,
而且这只能用于测试环境。
# aa.py
def func1():
print("func1")
import time, importlib
import aa
time.sleep(20)
# importlib.reload(aa)
aa.func1()
在 20 秒的等待时间里,修改 aa.py 中 func1 的内容,等待 test.py 的结果。
打开 importlib 注释,重新测试
把模块当做脚本执行
我们可以通过模块的全局变量__name__
来查看模块名:
当做脚本运行:__name__
等于'__main__'
当做模块导入:__name__
= 模块名
作用:用来控制.py 文件在不同的应用场景下执行不同的逻辑
if __name__ == '__main__':
模块搜索路径
python 解释器在启动时会自动加载一些模块,可以使用sys.modules
查看
模块的查找顺序是:内存中已经加载的模块->内置模块->sys.path 路径中包含的模块
在初始化后,python 程序可以修改sys.path
,路径放到前面的优先于标准库被加载
编译 python 文件
为了提高加载模块的速度,强调强调强调:提高的是加载速度而绝非运行速度。
python 解释器会在__pycache__
目录中下缓存每个模块编译后的版本,
格式为:module.version.pyc。
通常会包含 python 的版本号。
例如,在 CPython3.3 版本下,my_module.py
模块会被缓存成__pycache__/my_module.cpython-33.pyc
。
这种命名规范保证了编译后的结果多版本共存。
dir()
函数
内置函数dir()
可以用来查找模块中定义的名字,返回一个有序字符串列表
包
包是一种通过使用.模块名
来组织 python 模块名称空间的方式。
- 无论是 import 形式还是 from...import 形式,凡是在导入语句中(而不是在使用时)遇到带点的,都是关于包独有的导入语法,如
from moviepy.editor import *
- 包是目录级的(文件夹级),包的本质就是一个包含
__init__.py
文件的目录 - import 导入文件时,产生名称空间中的名字来源于文件,
- import 包时,产生的名称空间的名字同样来源于文件,即包下的
__init__.py
,导入包本质就是在导入该文件
强调:
- 而在 python2 中,包下没有
__init__.py
文件,import 包会报错 2. 创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包即模块
绝对导入和相对导入
内部互调
我们的最顶级包 glance 是写给别人用的,(结构图见最下)
然后在 glance 包内部也会有彼此之间互相导入的需求,这时候就有绝对导入和相对导入两种方式:
- 绝对导入:以 glance 作为起始
- 相对导入:用
.
或者..
的方式为起始(只能在一个包中使用,不能用于不同目录内)
# 绝对导入
from glance.cmd import manage
manage.main()
# 相对导入
from ..cmd import manage
manage.main()
外部测试结果:注意一定要在于 glance 同级的文件中测试
from glance.api import versions
注意:
在使用 pycharm 时,有的情况会为你多做一些事情
因而在测试时,一定要回到命令行去执行
__init__.py
决定的外部用例
单独导入包名称时,不会导入包中所有包含的所有子模块,
即不会自动导入同级所有目录和 python 文件(子模块)。
模块内方法的调用方式取决于各级的__init__.py
文件。
默认的
很多第三方模块的各级__init__.py
文件是如下书写的
# glance/__init__.py
from . import cmd
# glance/cmd/__init__.py
from . import manage
此时外部可以这样调用:
import glance
glance.cmd.manage.main()
惯例的
也可以这样书写使无论什么方式都可以调用:
glance/
├── __init__.py from .api import *
│ __version__ = "1.0.0"
│ __all__ = ['api','cmd','db']
├── api
│ │
│ ├── __init__.py __all__ = ['policy','versions']
│ │ from .versions import *
│ │
│ ├── policy.py
│ │
│ └── versions.py def get_now_version():
│ print("1.0.0")
│
├── cmd
│ │
│ ├── __init__.py __all__ = ['manage']
│ │
│ └── manage.py
│
└── db
│
├── __init__.py __all__ = ['models']
│
└── models.py
此时外部怎样调用都行:
>>> import glance
>>> glance.api.versions.get_now_version()
"1.0.0"
>>> glance.versions.get_now_version()
"1.0.0"
>>> glance.get_now_version()
"1.0.0"
也就是说,这里的__init__.py
里写了from .api import *
它的效果是含有 from . import api
的
当多重调用方式有重名的时候,深度优先:
如果get_now_version
函数就叫versions
>>> import glance
>>> glance.api.versions.versions()
AttributeError: 'function' object has no attribute 'versions'
>>> glance.versions.versions()
AttributeError: 'function' object has no attribute 'versions'
>>> glance.api.versions()
"1.0.0"
>>> glance.versions()
"1.0.0"
虚拟环境
创建虚拟环境并进入虚拟环境需要使用 Python 自带的venv
模块。首先,确保安装了 Python 3.3 版本及以上,然后打开终端(MacOS/Linux)或命令行(Windows),执行以下命令:
# 创建一个名为my_env的虚拟环境
python3 -m venv my_env
# 进入虚拟环境
# MacOS/Linux
source my_env/bin/activate
# Windows
my_env\Scripts\activate
在进入虚拟环境后,可以使用pip
安装各种包,这些包都会安装到当前虚拟环境中,不会影响全局环境。要退出虚拟环境,可以在终端或命令行中执行deactivate
命令。可以使用conda env list
查看已创建的虚拟环境列表。pip freeze
来查看当前环境已安装的包的版本