[Python]import使用的疑难杂症与包管理

[Python]import使用的疑难杂症与包管理,第1张

概述概念:模块与包模块module:一般是以.py为后缀的文件,也包括.pyo、.pyc、.pyd、.so和.dll后缀的文件,模块内定义了函数、类以及变量包package:包是含有若干个模块的文件夹,在工程项目用包管理模块可以避免模块名冲突__init__.py在Python工程项目中,如果一个文件夹下有__init__.py文件 概念:模块与包模块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 sysprint(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)>}
namespacelocal namespace:函数的命名空间,记录函数的变量global namespace:模块的命名空间,记录模块的变量(函数、类、导入的模块、模块级别的变量和常量)build-in namespace:包含build-in functionexceptions,可被任意模块访问

import方式影响我们使用包的方式正是namespace作用的体现:

from foo import bar  # 将模块foo中的函数/变量bar导入到当前模块的命名空间, 可以直接访问barimport foo  # 导入模块foo同时保留它自己的命名空间, 需要通过foo.bar的方式来访问bar
模块内部属性__doc__:文件注释__file__:当前文件路径__package__:导入文件的路径__cached__:导入文件的缓存路径__name__:导入文件的路径加文件名称__builtins__:包含内置函数python内置模块os:提供文件和目录等的系统级 *** 作sys:提供对解释器相关的 *** 作hashlib:提供加密相关的 *** 作,替代了md5sha模块shutil:提供文件、文件夹和压缩包等处理模块configparser:提供对特定配置的 *** 作logging:提供日志功能time datetime:提供时间相关 *** 作random:提供随机数 *** 作Jsonpickle:提供序列化 *** 作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.pybar.py在同一个非包(没有__init__.py文件)的目录下,那么它们之间可以互相import,不存在绝对导入和相对导入的问题。

在python3中建议使用绝对导入。

举个例子:

$ treemypackage├── __init__.py├── module_bar.py└── module_foo.py

在包mypackage内,如果module_bar要导入module_foo,那么有三种方式:

# 方法一: import module_foo# 方法二:# 如果是上层文件夹写.., 上上层文件夹写..., 以此类推from . import module_foo# 方法三:from mypackage import module_fooimport 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 sysprint(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_2from 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 osimport sysparent_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 syssys.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.pyTraceback (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 sysimport osssys.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]import使用的疑难杂症与包管理所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1187522.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-03
下一篇 2022-06-03

发表评论

登录后才能评论

评论列表(0条)

保存