模块和包,from 、import 的底层到底干了啥
函数,是对某些功能的封装,而模块是将特定函数、类、对象等封装成形式为一个独立的 .py文件。是为了更合理的规范代码,维护代码,同类型的功能封装在一起,即简单清晰,更重要的是能避免命名冲突
。
模块的出现方便了代码的组织管理,但是还存在像模块名冲突,模块杂乱,功能划分不具体等问题!为了解决这些问题,合理的管理Python的各种模块,引入了包管理的机制,即一个带有__init__.py文件的文件夹就称为一个包,包通过目录来组织模块。包可以包含包,包含模块,表现形式就是特殊的文件夹!
内建的包
urllib
、multiprocessing
、json
等第三方包
Django
、flask
、numpy
、Pilow
等用户自定义包
就是我们自己写的 带有 __init__.py
的目录了
包名和模块名尽量避免中文出现
包名和模块名尽量的避免和Python的内置模块名和包名冲突,尤其是包名
包和模块的命名,遵循Python变量命名规范
# 类UNIX/Linux/OSX
# 语法 pip3 install 包名==版本号 如果不确定具体的版本号 可用 ~= 代替 ==
pip3 install django~=2.0 # 安装最靠近2.0版本的django
pip3 install pymysql==0.93 # 安装 0.93 版本的 pymysql
# windows 和 Linux 没太大差别
通常我们会带上一些常用参数,或直接修改配置来指定pip
的行为
换pypi源
# 使用 -i 参数
pip install numpy -i https://mirrors.aliyun.com/pypi/simple/
从requirements.txt安装
# 使用 -r 参数 requirements.txt 文件必须在当前目录下
pip install -r requirements.txt
本地安装
pip install setup.py
从git安装
# pip install git+git:仓库地址
pip install git+git://github.com/blackmonkey121/verify.git
先看官方文档
This is a dictionary that maps module names to modules which have already been loaded. This can bemanipulated to force reloading of modules and other tricks. However, replacing the dictionary will notnecessarily work as expected and deleting essential items from the dictionary may cause Python to fail.
A list of strings that specifies the search path for modules. Initialized from the environment variablePYTHONPATH, plus an installation-dependent default.As initialized upon program startup, the first item of this list, path[0], is the directory containingthe script that was used to invoke the Python interpreter. If the script directory is not available (e.g.if the interpreter is invoked interactively or if the script is read from standard input), path[0] is theempty string, which directs Python to search modules in the current directory first. Notice that the scriptdirectory is inserted before the entries inserted as a result of PYTHONPATH.A program is free to modify this list for its own purposes. Only strings and bytes should be added to sys.path; all other data types are ignored during import.See also Module site This describes how to use .pth files to extend sys.path.
sys.modules中保存的是内存中已加载的模块名和模块地址的映射关系,它是一个字典
。字典的键就是模块名
(str)字典的值是模块对象(确切的说是它的引用
)
指定模块搜索路径的字符串列表
。从环境变量初始化python.path
的初始值,加上默认的安装位置。
import sys
for path in sys.path:
print(path)
# /Users/ms # 脚本所在的位置
# /Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip
# /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6
# /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload
# /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages
它在程序启动时初始化,此列表的第一项sys.path[0]
就是脚本文件所在的位置(当前位置)。
如果脚本目录不可用(例如如果以交互方式调用解释器或从标准输入读取脚本,则sys.path[0]
是空字符串,指示python首先搜索当前目录中的模块。
程序可以为自己的目的自由修改这个列表。只应将字符串和字节添加到sys.path
;导入期间忽略所有其他数据类型。
你似乎很熟悉他们,但他们的行为细节可能你知道的不足够。在这之前你要做好准备,他可能有些不太好理解。
# 以这句话为例
from PIL.image import getmodetype
1 解释器遇到from
,首先去sys.modules.keys()
搜索模块名PIL
,匹配不上就去sys.path
的每一个路径下匹配模块名PIL
。
2 匹配上,就在sys.modules
添加一个新的映射{‘PIL’:PIL <module ‘PIL‘ from ‘/Users/ms/Virtual/test.env/lib/python3.6/site-packages/PIL/init.py‘>},然后去执行包下的__init__.py
文件(如果__init__.py
文件中存在from
则继续递归的执行)。此时会将当前目录下的子模块全部加载到该包的命名空间中。
3 如果后面还有.
就到PIL下查找image
,一直重复下去直到遇见 import
关键字 停止from
4 此时from
的工作已经完成。
tips
: 在任意一步中没法找到包或模块都会报错,来终止程序。
# 以此为例
import PIL
1 解释器拿到import
后,首先去sys.modules.keys()
搜索模块名,匹配不上就去sys.path
的每一个路径下匹配PIL
,如果匹配上,就在sys.modules
创建一个新的映射, 到此时与 from
的作用是一致的。
2 然后Python会在内部创建这个对象(开辟空间),然后在这个空间解析这个对象(这也叫命名空间,就是导入模块时创建那个全局变量了),也就是在这个空间执行PIL
。也正因如此,import
后的对象我们可以直接使用。而from
不行。
3 如果import
后面跟的是个包,则会执行__init__.py
。如果是模块,则会直接执行该模块。
tips
:
如果在 import
过程中遇见 from
或import
都会按照他们各自的行为去执行。
使用from
语句可以将模块或包递归的添加到 sys.modules
。注意并没有执行代码,仅仅是执行了包下的__init__.py
文件。
# 以此为例
from matplotlib import pyplot as plt
from
会找寻 sys.modules
中是否存在 matplotlib
,如果没找到,将会按照 sys.path
中的路径去逐个遍历寻找 matplotlib
。(sys.path
的第一项总是当前目录,也就是说如果我们在当前目录下有一个文件叫matplotlib.py
或者某个包叫 matplotlib
,那将不会使用我们安装的 第三方matplotlib
因此 文件命名要注意这个问题。)
一旦找到,将不会往下寻找,直接将找到的模块
或包__init__.py
添加到sys.modules
中,同时将所有的子模块都添加到该包的命名空间中。如果所有sys.path
的目录下都没有找到将会报错。
遇见import
后该包的命名空间中寻找pyplot
是否存在。不存在则报错,存在将pyplot
对象加载到当前脚本的命名空间,并在此处执行pyplot
, 如果 导入语句属于全局,那么该包也会属于全局,反之,则属于局部
。
关于__init__.py
在 __init__.py
中可以像正常的Python文件那样写一些代码,这些会在每次导入时被默认的执行。通常不建议在 该文件下编写代码。
__all__
通常会被推荐写在 __init__.py
中,它能控制包的导入行为, 当使用模糊导入时,如果__init__.py
文件为空,将不会导入任何模块。如果__init__.py
写了 __all__
则只会导入 __all__
包含的对象。
__all__
是一个列表,列表对象应该是字符串。
原文:https://www.cnblogs.com/monkey-code/p/13158848.html