python中import与包治理
概念:模块与包
- 模块
module
:个别是以.py
为后缀的文件,也包含.pyo
、.pyc
、.pyd
、.so
和.dll
后缀的文件,模块内定义了函数、类以及变量 - 包
package
:包是含有若干个模块的文件夹,在工程项目用包治理模块能够防止模块名抵触
\_\_init\_\_.py
在Python工程项目中,如果一个文件夹下有__init__.py
文件就会认为该文件夹是一个包package
,这样能够不便组织工程文件,防止模块名抵触。
__init__.py
为空时仅用于标识以后这个文件夹是一个包package
__all__
变量指明当该包被import *
时,哪些模块module
会被导入- 能够利用
__init__.py
对外提供类型、变量及接口,对用户暗藏各个子模块的实现细节 - 当咱们
import
一个包时,会主动加载该包对应的__init__.py
,因而如果在其中做太简单的运算会造成不必要的开销
sys.modules
sys.modules
保护了一个已加载module
的字典,第二次加载该module
时能够间接从字典中查找,放慢执行速度。
import sys print(sys.modules) // 输入: {'random': <module 'random' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/random.pyc'>, 'subprocess': <module 'subprocess' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.pyc'>, 'sysconfig': <module 'sysconfig' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/sysconfig.pyc'>, 'gc': <module 'gc' (built-in)>}
namespace
local namespace
:函数的命名空间,记录函数的变量global namespace
:模块的命名空间,记录模块的变量(函数、类、导入的模块、模块级别的变量和常量)build-in namespace
:蕴含build-in function
和exceptions
,可被任意模块拜访
import
形式影响咱们应用包的形式正是namespace
作用的体现:
from foo import bar # 将模块foo中的函数/变量bar导入到以后模块的命名空间, 能够间接拜访bar import foo # 导入模块foo同时保留它本人的命名空间, 须要通过foo.bar的形式来拜访bar
模块外部属性
__doc__
:文件正文__file__
:以后文件门路__package__
:导入文件的门路__cached__
:导入文件的缓存门路__name__
:导入文件的门路加文件名称__builtins__
:蕴含内置函数
python内置模块
os
:提供文件和目录等的零碎级操作sys
:提供对解释器相干的操作hashlib
:提供加密相干的操作,代替了md5
和sha
模块shutil
:提供文件、文件夹和压缩包等解决模块configparser
:提供对特定配置的操作logging
:提供日志性能time
和datetime
:提供工夫相干操作random
:提供随机数操作json
和pickle
:提供序列化操作shelve
:提供简略kv
将内存数据通过文件长久化的性能
import形式
1. 简介
在Python中import
的罕用操作为:
import somemodule # 导入整个模块 from somemodule import somefunction # 从模块中导入单个函数 from somemodule import firstfunc, secondfunc, thirdfunc # 从模块中导入多个函数 from somemodule import * # 从模块中导入所有函数
2. 执行import的步骤
- 创立一个新的
module
对象 - 将该
module
对象插入sys.modules
- 装载
module
的代码 - 执行新的
module
中对应的代码
3. import的搜寻包程序
留神第三步装载module
代码时python解释器须要先搜寻到对应的.py
文件,搜寻程序为:
sys.path
:蕴含了以后脚本的门路和其余查找包(零碎库、第三方库等)的门路,你也能够在代码中通过sys.path.append()
动静增加搜寻门路PYTHONPATH
- 查看默认门路,比方Linux下为
/usr/local/lib/python/
4. 相对导入与绝对导入
相对导入和绝对导入的概念只针对于包内模块导入包内模块,留神如果foo.py
和bar.py
在同一个非包(没有__init__.py
文件)的目录下,那么它们之间能够相互import
,不存在相对导入和绝对导入的问题。
在Python3中倡议应用相对导入。
举个例子:
<code class="bash">$ tree mypackage ├── __init__.py ├── module_bar.py └── module_foo.py
在包mypackage
内,如果module_bar
要导入module_foo
,那么有三种形式:
# 办法一: import module_foo # 办法二: # 如果是下层文件夹写.., 上下层文件夹写..., 以此类推 from . import module_foo # 办法三: from mypackage import module_foo import mypackage.module_foo
- 对于python2而言,办法一和办法二都是绝对导入,成果一样,然而前者被称为隐式绝对导入,后者被称为显式绝对导入,办法三是相对导入(会在
sys.path
中的门路搜寻) - 对于python3而言,办法二是绝对导入,办法一和办法三都是相对导入,官网更举荐办法三
5. 包导入
包的导入和模块导入基本一致,只不过导入包时会执行__init__.py
。如果只是导入一个包import package
而不指名任何模块,且包中的__init__.py
没有其余的初始化操作,那么包上面的模块是无奈被主动导入的。
6. 间接运行与模块运行
以上面的我的项目为例:
$ tree . └── mypackage ├── __init__.py └── module_foo.py # module_foo.py内容如下: import sys print(sys.path)
咱们有两种形式运行module_foo.py
:
-m
参数示意run library module as a script,即以脚本的形式执行模块。
<code class="bash"># 间接运行: 第一个目录是模块module_foo所在的 $ python3 -B mypackage/module_foo.py ['/Users/didi/Desktop/MyProject/mypackage', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python37.zip', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload', '/Users/didi/Library/Python/3.7/lib/python/site-packages', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages'] # 模块运行: 第一个目录是以后门路 $ python3 -B -m mypackage.module_foo ['/Users/didi/Desktop/MyProject', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python37.zip', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload', '/Users/didi/Library/Python/3.7/lib/python/site-packages', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages']
实例:包之间模块援用的疑难杂症
1. 我的项目demo
假如以后你的工程文件目录如下(仅针对python3):
留神这里我的文件夹下并没有
__init__.py
,严格来讲它们并不是包,只是将分割严密的模块放在同一个文件夹下不便工程项目管理。
<code class="bash">. └── src ├── bar_package │ └── module_1.py ├── foo_package │ ├── module_2.py │ └── module_3.py └── main.py
# 留神 # 1) 所有模块都以src为根目录, 包含main.py(当然这只是我集体习惯) # 2) 引入形式都是相对引入(python3举荐应用) """ module_1.py: 空文件 """ """ module_2.py: import同个包内的module_3 """ from foo_package import module_3 # 援用同个包的模块 """ module_3.py: import另一个包内的module_1 """ from bar_package import module_1 # 跨包援用模块 if __name__ == "__main__": print("module_3 exec successfully!") """ main.py: import所有模块 """ from foo_package import module_3, module_2 from bar_package import module_1
下面就是通常我的项目文件包治理的形式,执行整个程序:
<code class="bash">python3 -B src/main.py
2. 问题:独自执行某个模块
如果要独自执行module_3.py
,这时候会报错:
<code class="bash">$ python3 -B src/foo_package/module_3.py Traceback (most recent call last): File "src/foo_package/module_3.py", line 1, in <module> from bar_package import module_1 # 跨包援用模块 ModuleNotFoundError: No module named 'bar_package'
回顾一下之前提到的import
查找包的门路,咱们有两种办法能够解决这个问题。
3. 办法一:通过模块运行的形式解决(举荐)
实质上咱们是心愿将module_3.py
这个模块作为脚本运行,所以咱们能够带上-m
参数:
<code class="bash">$ cd src # 代码中是以src为根目录的, 所以须要进入到src下 $ python3 -B -m foo_package.module_3 module_3 exec successfully!
4. 办法二:在sys.path中增加查找门路
后面的报错是找不到bar_package
的模块名,因为间接运行的话sys.path
第一个门路就是module_3.py
的门路,天然找不到它下层的bar_package
,咱们能够通过sys.path.append(..)
将它的下层目录也退出sys.path
,批改后的module_3.py
文件内容为:
"""module_3.py 实质上就是将module_3.py的下级目录退出到sys.path中, 这样就能够找到bar_package了 """ import os import sys parent_path = os.path.dirname(sys.path[0]) if parent_path not in sys.path: sys.path.append(parent_path) from bar_package import module_1 # 跨包援用模块 if __name__ == "__main__": print("module_3 exec successfully!")
须要留神的是,如果你应用的是如下这种写法还是可能呈现问题:
"""module_3.py """ import sys sys.path.append("../") from bar_package import module_1 # 跨包援用模块 if __name__ == "__main__": print("module_3 exec successfully!") # 进入到module_3.py所在的目录, 输入失常: $ src/foo_package $ python3 -B module_3.py module_3 exec successfully! # 间接在根目录下执行会报错: $ python3 -B src/foo_package/module_3.py Traceback (most recent call last): File "src/foo_package/module_3.py", line 3, in <module> from bar_package import module_1 # 跨包援用模块 ModuleNotFoundError: No module named 'bar_package'
另一种简洁的写法是:
import sys import oss sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
5. 尽量不要应用绝对援用
Python3不倡议应用绝对援用,最好遵循肯定的开发标准,不要在代码中混用相对援用与绝对援用。
Reference
[1] https://www.gaodaima.com/weixin_…
[2] https://zhuanlan.zhihu.com/p/…
[3] https://www.cnblogs.com/schip…
[4] https://www.gaodaima.com/p/88b…