[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中建议使用绝对导入。
举个例子:
$ 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,即以脚本的方式执行模块。
# 直接运行: 第一个目录是模块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
,严格来讲它们并不是包,只是将联系紧密的模块放在同一个文件夹下方便工程项目管理。
.
└── 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
上面就是通常项目文件包管理的方式,执行整个程序:
python3 -B src/main.py
2. 问题:单独执行某个模块
如果要单独执行module_3.py
,这时候会报错:
$ 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
参数:
$ 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://blog.csdn.net/weixin_38256474/article/details/81228492
[2] https://zhuanlan.zhihu.com/p/115350758
[3] https://www.cnblogs.com/schips/p/12148092.html
[4] https://www.jianshu.com/p/88b0f6f28f25
[Python]import使用的疑难杂症与包管理的更多相关文章
- Python札记 -- 使用easy_install进行模块/包管理
今天在阅读以前项目代码时,发现里面使用的第三方模块的参数相当诡异,总是对不上.经过分析之后,发现是自己安装的第三方模块跟项目使用的版本不一致.在Python中进行模块/包管理的话,就不得不提到easy ...
- 【python基础语法】模块和包管理,文件的操作(第8天课堂笔记)
''' 模块和包管理 模块和包的定义: 模块:模块是一个Python文件,以.py结尾,包含了Python对象定义和Python语句 包:Python中的包就是一个包含__init__.py文件的目录 ...
- Python的支持工具[0] -> 环境包管理工具[0] -> pip
pip包管理工具 / pip Package Management Tools pip是一个Python包管理工具,主要是用于安装PyPI上的软件包,可以替代easy_install工具. 1 pip ...
- Python的支持工具[0] -> 环境包管理工具[1] -> Anaconda
Anaconda包管理工具 / Anaconda Package Management Tools Anaconda is the world’s most popular Python data s ...
- nodejs,python,sublime和Eclipse的包管理器
Python的包管理器叫pip. 首先安装Python运行环境Python 3.7.0:https://www.python.org/downloads/release/python-370/ Pyt ...
- python文件打包格式,pip包管理
1..whl是python文件的一种打包格式, 在有些情况下,可以将文件的后缀名改为.zip并解压 2.cmd中,提示pip版本太低,先升级pip pip install --upgrade pi ...
- 01 Python简介、环境搭建及包管理(一)
一.Python简介 1. Python的特点: 是一门动态.解释型.强类型语言 动态:在运行期间才做数据检查(不用提前声明变量)- 静态语音(C/Java):编译时检查数据类型(编码时需要声明变量类 ...
- Python包管理工具小结
此文已由作者张耕源授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 作为一名接触Python有一段时间的初学者,越来越体会到Python的方便之处,它使人能更 多的关注业务本身 ...
- python包管理之Pip安装及使用-1
Python有两个著名的包管理工具easy_install.py和pip.在Python2.7的安装包中,easy_install.py是默认安装的,而pip需要我们手动安装. pip可以运行在Uni ...
随机推荐
- PAUL ADAMS ARCHITECT:澳洲房贷最低利率来袭
11月3日澳洲储备银行宣布将官方现金利率从0.25%降至0.1%,破历史最低纪录.此次澳洲储备银行降息的目的主要是为了刺激经济走出全球经济危机引发的衰退.据了解,这已经是澳洲今年第三次降息,也是自20 ...
- 初学c++,vc++6.0必备!
文章首发 | 公众号:lunvey 作为一个纯粹的萌新,工作需要,刚接触到c++. 按照以往的经验,配置一个开发环境是首要的,其次便是边学边敲. c++入门书籍寻找了一堆,发现了一个共同点,在Wind ...
- 前端监控SDK开发分享
目录 前言 收集哪些数据 性能 错误 辅助信息 小结 客户端SDK(探针)相关原理和API Web 微信小程序 编写测试用例 单元测试 流程测试 提供Web环境的方式 Mock Web API的方式 ...
- (转载)VoLTE简介
转载地址:http://www.360doc.cn/article/2909773_637471256.html,本文介绍了移动通信领域相关概念,如CS.PS.VoIP.VoLTE.IMS.CSFB. ...
- 深入浅出的JS执行机制(图文教程)
前序 作为一个有理想有抱负的前端攻城狮,想要走向人生巅峰,我们必须将我们使用的功法练到天人合一的地步.我在们日常工作中,使用最多的语言就是JavaScript了,为了写出完美的.能装逼的代码,我们必须 ...
- Power Query 导入多源数据
导入方法: 导入数据库文件: 修改加载方式: 其他类型数据处理方式类似
- HTTP状态响应码解析
# HTTP响应状态码 ## 1xx:临时响应 #### 表示临时响应并需要请求者继续执行操作的状态代码. 100 **继续**请求者应当继续提出请求.服务器返回此代码表示已收到请求的第一部分,正在等 ...
- 【springboot读取配置文件】@ConfigurationProperties、@PropertySource和@Value
概念: @ConfigurationProperties : 是springboot的注解,用于把主配置文件中配置属性设置到对于的Bean属性上 @PropertySource :是spring的注解 ...
- Java(JDK/Tomcat/Maven)运行环境配置及工具(idea/eclipse)安装
Java (计算机编程语言) Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,因此Java语言具有功能强大和简单易用两个特征. Java语 ...
- 后端程序员之路 40、Pthreads
POSIX线程(POSIX threads),简称Pthreads,是线程的POSIX标准.线程这个东西在操作系统原理里讲得比较清楚了,再加上对windows那一套进程线程的东西比较清楚,所以这里还是 ...