当一个项目中有很多模块时,需要再进行组织时。可将模块功能类似的模块放到一起形成“包”。本质上,“包”就是一个必须有__init__.py文件的文件夹。
包下面可以包含“模块”,也可以再包含“子包”。就像文件夹下可以有文件也可以有子文件夹一样。
__init__.py
文件的作用:
# hello.py
class AddSub:
def __init__(self, a, b):
self.a = a
self.b = b
def add(self):
return self.a+self.b
def sub(self):
return self.a-self.b
# hello1.py
def add(a, b):
return a + b
def sub(a, b):
return a - b
# 123.py
from my_test import hello, hello1
test0 = hello.AddSub(2, 3)
print(test0.add())
print(test0.sub())
test1_add = hello1.add(2, 3)
test1_sub = hello1.sub(2, 3)
print(test1_add)
print(test1_sub)
"""
运行结果:
5
-1
5
-1
Process finished with exit code 0
"""
import * 这样的语句理论上时希望文件系统找出保重所有的子模块,然后导入它们。这可能会花费长时间等待。Python解决方案是提供一个明确的包索引。
这个索引由__init__.py定义,该变量为一列表。
以下是使用__init__.py进行模糊导入的示例:
# hello.py
class AddSub:
def __init__(self, a, b):
self.a = a
self.b = b
def add(self):
return self.a+self.b
def sub(self):
return self.a-self.b
# hello1.py
def add(a, b):
return a + b
def sub(a, b):
return a - b
# __init__.py
__all__ = ["hello", "hello1"]
# 123.py
from my_test import *
test0 = hello.AddSub(2, 3)
print(test0.add())
print(test0.sub())
test1_add = hello1.add(2, 3)
test1_sub = hello1.sub(2, 3)
print(test1_add)
print(test1_sub)
"""
运行结果:
5
-1
5
-1
Process finished with exit code 0
"""
相对导入,使用“.”和“..”,例如:
绝对导入,不使用“.”和“..”的其它所有情况。
绝对导入
时解释器判断一个文件夹是包而不是普通文件夹必须满足一个条件:
不使用“.”和“..”的其它所有情况本质上
都属于绝对导入,都需要在sys.path默认的搜索路径(有多个路径)中按一定的顺序查找导入的模块
。
错误:
ModuleNotFoundError: No module named 'xxxx'
错误示例:
所用包与模块的结构如下图所示:
#pic_center)
# hello2.py
import hello3
# hello3.py
print(r"import_找不到被导模块\my_test\hello3.py")
# main.py
from my_test import hello2
# 由于sys.path默认的模块搜索路径中不存在路径“E:\Project\PyCharm\test\import_找不到被导模块\my_test”,导致找不到hello3模块
# 主目录为:“E:\Project\PyCharm\test”
# 当前目录:“\Project\PyCharm\test\import_找不到被导模块”
以main.py作为主函数入口,运行main.py
"""
运行结果:
Traceback (most recent call last):
File "E:/Project/PyCharm/test/import_找不到被导模块/main.py", line 13, in
from my_test import hello2
File "E:\Project\PyCharm\test\import_找不到被导模块\my_test\hello2.py", line 1, in
import hello3
ModuleNotFoundError: No module named 'hello3'
Process finished with exit code 1
"""
解决方案:
(1) 使用导入时完整路径
(绝对路径)
# 将hello2.py中原来代码修改成以下代码
from import_找不到被导模块.my_test import hello3
# hello3.py
print(r"import_找不到被导模块\my_test\hello3.py")
# main.py
from my_test import hello2
以main.py作为主函数入口,运行main.py
"""
运行代码:
import_找不到被导模块\my_test\hello3.py
Process finished with exit code 0
"""
(2) 使用sys.path.append(“需要添加的路径”)
# hello2.py
import hello3
# hello3.py
print(r"import_找不到被导模块\my_test\hello3.py")
# 将main.py中原来代码修改成以下代码
import sys
sys.path.append(r"E:\Project\PyCharm\test\import_找不到被导模块\my_test")
from my_test import hello2
以main.py作为主函数入口,运行main.py
"""
运行代码:
import_找不到被导模块\my_test\hello3.py
Process finished with exit code 0
"""
相对导入
时解释器判断一个文件夹是包而不是普通文件夹必须满足两个条件:
错误1.1
ModuleNotFoundError: No module named '__main__.mul_div'; '__main__' is not a package
# mul_div.py
class MuLDiv:
def __init__(self, a, b):
self.a = a
self.b = b
def mul(self):
return self.a*self.b
def div(self):
return self.a/self.b
# main_0.py
from .mul_div import MuLDiv
mul_div = MuLDiv(2, 3)
print(mul_div.mul())
print(mul_div.div())
以main_0.py作为主函数入口,运行main_0.py
"""
运行结果:
Traceback (most recent call last):
File "E:/Project/PyCharm/test/relative_import_test/my_test/sub_1_my_test/main_0.py", line 1, in
from .mul_div import MuLDiv
ModuleNotFoundError: No module named '__main__.mul_div'; '__main__' is not a package
Process finished with exit code 1
"""
错误1.2
# mul_div.py
class MuLDiv:
def __init__(self, a, b):
self.a = a
self.b = b
def mul(self):
return self.a*self.b
def div(self):
return self.a/self.b
# 将main_0中代码改为
from . import mul_div
mul_div = mul_div.MuLDiv(2, 3)
print(mul_div.mul())
print(mul_div.div())
以main_0.py作为主函数入口,运行main_0.py
"""
运行结果:
Traceback (most recent call last):
File "E:/Project/PyCharm/test/relative_import_test/my_test/sub_1_my_test/main_0.py", line 1, in
from . import mul_div
ImportError: cannot import name 'mul_div'
Process finished with exit code 1
"""
错误1.1和错误1.2分析:
错误1.1和错误1.2本质是同一个问题导致。
相对导入基于当前模块
的名称,因为主模块总被命名为"__main__"。当我们从主模块启动时,Python就识图用"__main__“替换”.",于是from . import mul_div实际变成了from __mian__ import mul_div ,而from .mul_div import MulDiv 实际变成了from __main__.mul_div import MulDiv,这当然是找不到的。
尝试解决错误1.1和错误1.2,此时遇到问题1.3,过程如下:
问题1.3
ImportError: attempted relative import with no known parent package
错误示例:
所用包与模块的结构如下图所示:
在sub_1_my_test包下新建主函数入口模块main_0_0.py
# mul_div.py
class MuLDiv:
def __init__(self, a, b):
self.a = a
self.b = b
def mul(self):
return self.a*self.b
def div(self):
return self.a/self.b
# 为了测试解决错误1.1和错误1.2,此处将main_0.py代码修改成
from . import mul_div
from .mul_div import MuLDiv
mul_div = mul_div.MuLDiv(2, 3)
print(mul_div.mul())
print(mul_div.div())
mul_div = MuLDiv(6, 2)
print(mul_div.mul())
print(mul_div.div())
# main_0_0.py
import main_0
以main_0_0.py作为主函数入口,运行min_0_0.py
"""
运行结果:
Traceback (most recent call last):
File "E:/Project/PyCharm/test/relative_import_test/my_test/sub_1_my_test/main_0_0.py", line 1, in
import main_0
File "E:\Project\PyCharm\test\relative_import_test\my_test\sub_1_my_test\main_0.py", line 1, in
from . import mul_div
ImportError: attempted relative import with no known parent package
Process finished with exit code 1
"""
问题分析错误1.3:
忽略了相对导入时一个文件夹成为包的一个条件–该文件夹下不能含有顶层模块(含有主函数的模块)。此处是因为“.”导入时需要将sub_1_my_test看作一个包,但由于主函数的入口main_0_0.py在sub_1_my_test下,导致sub_1_my_test不能被解释器解释为一个包,因此出错
。
解决问题1.3:
将主函数入口模块(顶级模块)放到相对导入时所涉及的包的外层包,即此处相对导入所涉及的包为sub_1_my_test,而外层包为my_test以及更上层的包。
具体操作如下:
将main_0_0.py放到my_test包下,修改后的包和模块的结构如下图所示:
# mul_div.py
class MuLDiv:
def __init__(self, a, b):
self.a = a
self.b = b
def mul(self):
return self.a*self.b
def div(self):
return self.a/self.b
# 为了测试解决错误1.1和错误1.2,此处将main_0.py代码修改成
from . import mul_div
from .mul_div import MuLDiv
mul_div = mul_div.MuLDiv(2, 3)
print(mul_div.mul())
print(mul_div.div())
mul_div = MuLDiv(6, 2)
print(mul_div.mul())
print(mul_div.div())
# 将main_0_0.py修改成
from sub_1_my_test import main_0
以main_0_0.py作为主函数入口,运行main_0_0.py
"""
运行结果:
6
0.6666666666666666
12
3.0
Process finished with exit code 0
"""
错误2:
ValueError: attempted relative import beyond top-level package
# mul_div.py
class MuLDiv:
def __init__(self, a, b):
self.a = a
self.b = b
def mul(self):
return self.a*self.b
def div(self):
return self.a/self.b
# add_sub.py
class AddSub:
def __init__(self, a, b):
self.a = a
self.b = b
def add(self):
return self.a+self.b
def sub(self):
return self.a-self.b
# main_0.py
from . import mul_div
from .. import add_sub
mul_div = mul_div.MuLDiv(2, 3)
print(mul_div.mul())
print(mul_div.div())
add_sub = add_sub.AddSub(2, 3)
print(add_sub.add())
print(add_sub.sub())
# main_1.py
from sub_1_my_test import main_0
以main_1.py作为主函数入口,运行main_1.py
"""
运行结果:
Traceback (most recent call last):
File "E:/Project/PyCharm/test/relative_import_test/my_test/main_1.py", line 1, in
from sub_1_my_test import main_0
File "E:\Project\PyCharm\test\relative_import_test\my_test\sub_1_my_test\main_0.py", line 2, in
from .. import add_sub
ValueError: attempted relative import beyond top-level package
Process finished with exit code 1
"""
问题分析错误2:
错误2的本质与错误1.3相同,都是忽略了相对导入时一个文件夹成为包的一个条件–该文件夹下不能含有顶层模块(含有主函数的模块)。此处是因为“..”导入时需要将my_test看作一个包,但由于主函数的入口main_1.py在my_test下,导致my_test不能被解释器解释为一个包,因此出错
。
解决问题2:
将主函数入口模块(顶级模块)放到相对导入时所涉及的包的外层包,即此处相对导入所涉及的包为sub_1_my_test、my_test,而外层包为relative_import_test以及更上层的包。
具体操作如下:
将main_1.py放到relative_import_test包下,修改后的包和模块的结构如下图所示:
# 只需要将main_1.py修改成以下代码,其它模块代码不变
from my_test.sub_1_my_test import main_0
以main_1.py作为主函数入口,运行main_1.py
"""
运行结果:
6
0.6666666666666666
5
-1
Process finished with exit code 0
"""
区别绝对导入与相对导入的关键点:
绝对导入:
相对导入:
相对导入
中,主函数入口文件(顶级模块)导致其所在文件夹不会被解释器解释为包。为了在项目中遇到以上导包问题,此处建议在实施时尽可能参照规范建立项目:
项目名
| -> 包名称(同项目名)
| -> __init__.py
| -> 内部包1
| -> __init__.py
| -> A.py
| -> B.py
| -> 内部包2
| -> __init__.py
| -> C.py
| -> 内部包3
| -> __init__.py
| -> D.py
| -> 测试文件夹
| -> test.py
| -> 入口运行文件(main.py)
如图中的项目结构,规范如下:
## 以A.py为例
from 项目名包.内部包2.内部包3 import D
D.func()
在"from YY import XX"这样的代码中,无论是XX还是YY,只要被python解释器视作package,就会首先调用该package的__init__.py文件。如果都是package,则调用顺序是YY,XX。
示例:
所用包与模块的结构如下图所示:
#pic_center)
# my_test\sub_0_my_test\__init__.py
print(r"导入包_模块时__init__文件和被导模块的执行过程\my_test\sub_0_my_test\__init__.py")
# hello_0.py
print("hello_0.py")
# my_test\__init.py
print(r"导入包_模块时__init__文件和被导模块的执行过程\my_test\__init__.py")
# main.py
from my_test.sub_0_my_test import hello_0
以main.py作为主函数入口,运行main.py
"""
运行结果:
导入包_模块时__init__文件和被导模块的执行过程\my_test\__init__.py
导入包_模块时__init__文件和被导模块的执行过程\my_test\sub_0_my_test\__init__.py
hello_0.py
Process finished with exit code 0
"""
import语句:
from…import…语句:
总结
对于import…来说
对于from…import…来说
假设a,b为包名,c为模块名,c中有个类C
import语句
import a.b.c
# 实例化一个C实例对象
test = a.b.c.C() # 不能写为test = C()
from…import…语句
from a.b.c import C
# 实例化一个C实例对象
test = C()
from a.b import c
# 实例化一个C实例对象
test = c.C()
from a import b
# 实例化一个C实例对象
test = b.c.C()
模块名不能与其所在的包名同名,否则会报错。
具体原因目前不知。两种解决方案。
(1)重命名。
(2)
import sys
_cpath_ = sys.path[0] #获取当前路径
sys.path.remove(_cpath_) #删除
from jira import JIRA
sys.path.insert(0, _cpath_) #恢复
sys.path.remove(_cpath_) #删除 这一条可以将path中的第一条删除
sys.path.insert(0, _cpath_) #恢复这一条可以将path恢复成原来的样子
原理
程序总是将上级目录内容定位最高优先级,只要将它暂时屏蔽掉就可以了
比如:"-"
/ "("
/ ")"
import 和“__import__”都是用来导入module的,但是二者还是有所不同, 可以查看帮助文档来了解其不同.
先通过 help(“import”) 查看其帮助,可以找到如下的说明:
The basic import statement (no “from” clause) is executed in two
steps:
- find a module, loading and initializing it if necessary
- define a name or names in the local namespace for the scope where the “import” statement occurs.
上面描述了import 做的两步工作,首先是 查找一个模块或包,然后初始化这个模块或包,我个人对这一步的理解是这样的:
再通过help(import) 查看其帮助,我们可以看到如下的片段:
import(name, globals=None, locals=None, fromlist=(), level=0) -> module
Import a module. Because this function is meant for use by the Python
interpreter and not for general use it is better to use
importlib.import_module() to programmatically import a module.
这里可以看到 import 是一个方法,这是__builtins__ 模块内置的一个方法,返回值是一个module,所以可以通过 import(ModuleName).Method的方式来使用,比如在一些项目中会看到__import(“pkg_resources”)__.run_script() 这样的方式来使用这种导入
既然两者都可以导入module, 那么使用上怎么选择呢?
import 导入模块之后,就在相应的namespace 中存在了,除非明确用del 从当前的namespace中移出,否则始终可以访问到;
而用__import__ 导入,返回的是一个 模块,可以引用这个模块,但是这个模块并不会“驻留”到相应的namespace. 也就是常用于 动态加载模块,从而可以根据需要 动态使用,节省系统资源;比如某个python 程序需要引用20个模块,但是这20个模块都只是引用其中某一个方法,那么完全没有必要 让这20个模块一直驻留在相应的namespace, 从而节省系统资源;
以上内容部分摘自以下参考博客
[参考博客及python手册]
ModuleNotFoundError: No module named ‘main.xxx’; ‘main’ is not a package问题
Python踩坑之路
Python中import的用法
python3.6.1手册–6.4. 包
Python包的相对导入时出现错误的解决方法
理解python的import与__import__
python文件名与包名冲突