文章目录
写了这么久的python,发现连最基本的问题还没有搞懂。在写比较大的项目点时候,可能会涉及到不同包下模块的引用,经常会出现无法引用自己写的模块的问题。
核心就是两个概念:模块和包。
模块:实现一个功能,本质是*.py文件
包:定义了一个由模块和子包组成的Python应用程序执行环境,本质就是一个有层次的文件目录结构(必须带有一个__init__.py文件)。
在较大型的项目中,我们的代码不可能写在一个脚本中,需要根据功能拆分成不同部分,对于重复使用的功能就会涉及到模块之间的调用,这时就会出现各种包、模块引用不成功的bug,下面以具体例子记录下我的理解。
一、pycharm和cmd中的使用
1.1、项目结构目录
- 最外层文件夹名称test_package,包含文件夹package1、package2,文件test_all.py。
- package1下有文件__init.py、test1_1.py、test_pak1.py
test1_1.py:加法运算
def plus(a,b):
return a+b
test_pak1.py :调用test1_1.py中的加法运算
from test1_1 import plus
if __name__ == '__main__':
a=4
b=5
c=plus(a,b)
print(c)
package2下有__init__.py、test2_1.py、test_pak2.py
test2_1.py:减法运算
def minus(a,b):
return a-b
test_pak2.py:分别调用test2_1.py下的减法运算和package1下的test1_1.py中的加法运算
from package2.test2_1 import minus
from package1.test1_1 import plus
if __name__ == '__main__':
a=4
b=2
print(minus(a,b))
print(plus(a,b))
1.2、同级目录下的调用
test_pak1.py调用同级目录下test1_1.py中的plus()函数。
运行test_pak1.py
在cmd中运行结果一样。
1.3、不同级的调用
package2下的test_pak2.py调用package1下的test1_1.py中的plus()函数。
运行test_pak2.py
在cmd中运行会报错,提示找不到package2,出错原因我们稍后解释。
1.4、父级目录调用子级目录下的模块
运行test_all.py
在cmd中可以也正常运行。
二、出错原因和解决方法
2.1 出错原因
1.3节中不同包中的模块相互调用为啥在pycharm中可以执行,在cmd中就提示找不到package2了呢,这里要简单解释一下import的机制(具体原理会单独写篇文章介绍)。总的来说要想正确import模块,模块必须在sys.path中能被找到。
可以简单的认为 import 的查找顺序是:
1、内置模块
2、.py 文件所在目录
3、环境变量 PYTHONPATH中列出的目录(类似环境变量 PATH,由用户定义,默认为空);
4、pip 或 easy_install 安装的包
我们在test_pak2.py中打印出sys.path。
import sys
for i in sys.path:
print(i)
from package2.test2_1 import minus
from package1.test1_1 import plus
if __name__ == '__main__':
a=4
b=2
print(minus(a,b))
print(plus(a,b))
在pycharm中运行
可以看到不光是test_pak2.py所在的文件夹package2被加载,文件夹package2的父级文件夹test_package,再上一级文件夹Python也都被加载进来了,因此包package1、package2可以被sys.path找到,并成功加载。
from package2.test2_1 import minus
from package1.test1_1 import plus
在cmd中测试
可以看到在cmd中执行test_pak2.py时,sys.path只是把test_pak2.py所在的路径D:\project\Python\test_package\package2加入进来了,在这个路径下找package1、package2当然就找不到了。
那么为啥pycharm可以将项目所在路径全部加载进sys.path而cmd只能加载py脚本所在路径?
原因是我在pycharm中把test_package设置为source root了。
在pycharm中对一个目录不设置Sources Root时,就是一个带点的文件夹。
设置Sources Root时是不带点的蓝色文件夹,因此很容易区分。
现在我们把test_package的Sources Root去掉,重新在pycharm里执行test_pak2.py
可以看到这时错误就和cmd中一样了。
2.2 解决办法
如果就是想在cmd中使用不同包之间的模块调用方式,问题该怎么解决呢?
第一种方法:sys.path.append()
import sys
sys.path.append("..")
for i in sys.path:
print(i)
from package2.test2_1 import minus
from package1.test1_1 import plus
if __name__ == '__main__':
a=4
b=2
print(minus(a,b))
print(plus(a,b))
结果
“..” 表示当前目录的上一级目录,这句话的意思是将test_pack2.py的上一级目录加载进sys.path中。当然也可以用绝地路径。sys.path.append(r"D:\project\Python\test_package")
结果
第二中方法:设置环境变量PYTHONPATH(记得重启)
第一个应该是安装qgis的时候自动添加的,这也是为啥之前输出sys.path的时候会有qgis的路径。
直接运行python test_pak2.py
第三种方法:使用-m方式运行,需要在test_pak2.py的上一级目录执行