2021-01-27
首先我们需要了解一下基础:
参考我的博客:https://www.cnblogs.com/zhangchao0515/p/14333370.html
接下来,我们讲的重点是 Python 模块(Module)调用
一、我们先创建一个测试项目python-module
1、搜索路径
当你导入一个模块,Python 解析器对模块位置的搜索顺序是:
- 1、当前目录
- 2、如果不在当前目录,Python 则搜索在 shell 变量 PYTHONPATH 下的每个目录。
- 3、如果都找不到,Python会察看默认路径。UNIX下,默认路径一般为/usr/local/lib/python/。
模块搜索路径存储在 system 模块的 sys.path 变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。
2、Python中的包
包是一个分层次的文件目录结构,它定义了一个由模块及子包,和子包下的子包等组成的 Python 的应用环境。
简单来说,包就是文件夹,但该文件夹下必须存在 __init__.py 文件, 该文件的内容可以为空。__init__.py 用于标识当前文件夹是一个包。
3、目录结构如下
目录是D:Projectspythonvscodepython-module
python-module src main package1 __init__.py module1.py package2 package3 __init__.py module2.py __init__.py module2.py moduletest1.py moduletest2.py moduletest3.py moduletest4.py test
4、文件说明
def fun1(): print ("我是来自 module1")
module2.py 如下所示:
def fun2(): print ("我是来自 module2")
module3.py 如下所示:
def fun3(): print ("我是来自 module3")
5、执行方法
1)打开cmd
2)切换到要执行的py目录下
3)执行py
例如:
python moduletest1.py
一、python默认的模块导入执行过程
我们知道了搜索路径,我们直接考虑第一种搜索:
当你导入一个模块,Python 解析器对模块位置的搜索顺序是:
- 1、当前目录
- 2、如果不在当前目录,Python 则搜索在 shell 变量 PYTHONPATH 下的每个目录。
- 3、如果都找不到,Python会察看默认路径。UNIX下,默认路径一般为/usr/local/lib/python/。
模块搜索路径存储在 system 模块的 sys.path 变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。
会先搜索当前目录,其实就是把当前目录临时添加到环境变量,作用域仅限于该py文件。由此引入了方法1
1. python默认的模块导入执行过程-- 1) 要么是同一目录下,可以相互调用 2) 要么是在子目录下,可以从当前模块往下 (都是需要__init__.py,而且是根据python自己的机制) 针对1)和2)是默认的机制-- python会自动将该文件的的路径临时添加到环境变量中,类似在文件中 sys.path.append('当前py文件目录')
1、首先我们先打印一下 sys.path看一下环境
moduletest1.py如下
import sys # 测试第一种:使用python默认的模块导入机制 # 只能导入同一目录或子目录下的模块,效果类似 sys.path.append('当前文件所在的目录') -- 作用域只是当前py文件 # python moduletest1.py -- 作用域只是当前的python if __name__ == '__main__': print(sys.path)
打开cmd,切换到该文件所在目录,执行
python moduletest1.py
结果如下所示:
我们可以看到,默认就会把该目录添加到环境变量中。
2、我们直接引入模块module2.py和module3.py
moduletest1.py如下
import sys # 测试第一种:使用python默认的模块导入机制 # 只能导入同一目录或子目录下的模块,效果类似 sys.path.append('当前文件所在的目录') -- 作用域只是当前py文件 # python moduletest1.py -- 作用域只是当前的python # 1. 这个是在同一目录下,可以相互调用。效果类似在该文件中添加 sys.path.append('当前文件所在的目录') -- 作用域只是当前py文件 from module2 import fun2 # 2. 这个是在子目录下,只能自己调用子目录的,反过来不行。效果类似在该文件中添加 sys.path.append('当前文件所在的目录') -- 作用域只是当前py文件 from package3.module3 import fun3 # 3. 会报错,不识别模块package1 # from package1.module1 import fun1 if __name__ == '__main__': print(sys.path) fun2() fun3() # fun1()
打开cmd,切换到该文件所在目录,执行
python moduletest1.py
结果如下所示:
这是因为:搜索路径,会先搜索当前目录
3、我们再引入模块module1.py
import sys # 测试第一种:使用python默认的模块导入机制 # 只能导入同一目录或子目录下的模块,效果类似 sys.path.append('当前文件所在的目录') -- 作用域只是当前py文件 # python moduletest1.py -- 作用域只是当前的python # 1. 这个是在同一目录下,可以相互调用。效果类似在该文件中添加 sys.path.append('当前文件所在的目录') -- 作用域只是当前py文件 from module2 import fun2 # 2. 这个是在子目录下,只能自己调用子目录的,反过来不行。效果类似在该文件中添加 sys.path.append('当前文件所在的目录') -- 作用域只是当前py文件 from package3.module3 import fun3 # 3. 会报错,不识别模块package1 from package1.module1 import fun1 if __name__ == '__main__': print(sys.path) fun2() fun3() fun1()
打开cmd,切换到该文件所在目录,执行
python moduletest1.py
结果如下所示:
那是因为他不会往上级目录搜索
二、通过sys.path.append 或者sys.path.exclude临时导入环境变量
当你导入一个模块,Python 解析器对模块位置的搜索顺序是:
- 1、当前目录
- 2、如果不在当前目录,Python 则搜索在 shell 变量 PYTHONPATH 下的每个目录。
- 3、如果都找不到,Python会察看默认路径。UNIX下,默认路径一般为/usr/local/lib/python/。
模块搜索路径存储在 system 模块的 sys.path 变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。
通过sys.path.append 或者sys.path.exclude临时导入环境变量,它的作用域也只是该py文件。
1、按照惯例先看一下sys.path
moduletest2.py如下
import sys
# 测试第二种:
# 在文件中添加 sys.path.append('项目根目录'), sys.path.append('当前文件所在的目录') -- 作用域只是当前py文件
# python moduletest2.py -- 作用域只是当前的python
if __name__ == '__main__':
print(sys.path)
打开cmd,切换到该文件所在目录,执行
python moduletest1.py
结果如下所示:
2、我们直接引入模块module1.py和module2.py和module3.py
moduletest2.py如下
import sys import os # 测试第二种: # 在文件中添加 sys.path.append('项目根目录'), sys.path.append('当前文件所在的目录') -- 作用域只是当前py文件 # python moduletest2.py -- 作用域只是当前的python # 获取当前文件的目录 # curpath = os.path.abspath(os.path.dirname(__file__)) # 获取项目根目录 D:\Projects\python\vscode\python-module projectpath = os.path.abspath(os.path.dirname(__file__)+r'../../../') # 1. 在该文件中添加 sys.path.append('项目根目录') -- 作用域只是当前py文件 sys.path.append(projectpath) from src.main.package2.module2 import fun2 # 2. 在该文件中添加 sys.path.append('当前文件所在的目录') ,python默认会把该目录加入,不需要这步操作 -- 作用域只是当前py文件 # sys.path.append(curpath) # from module2 import fun2 from src.main.package2.package3.module3 import fun3 from src.main.package1.module1 import fun1 if __name__ == '__main__': print(sys.path) fun2() fun3() fun1()
打开cmd,切换到该文件所在目录,执行
python moduletest1.py
结果如下所示:
所以,环境变量里面一个是自动添加的目录,一个是我们自己添加的目录,两个同样的作用域是该py文件。且都是临时的。
自动添加的:'D:\Projects\python\vscode\python-module\src\main\package2' 我们添加的:'D:\Projects\python\vscode\python-module'
由此我们可以添加'D:\Projects\python\vscode\python-module'目录下的模块,需要引入相关包。该方法不能动源码结构,否则要大量修改源码。
当然我们的包名引入还可以用一的,因为也存在 'D:\Projects\python\vscode\python-module\src\main\package2'。
3、混用这两个路径
moduletest2_1.py如下
import sys import os # 测试第二种: # 在文件中添加 sys.path.append('项目根目录'), sys.path.append('当前文件所在的目录') -- 作用域只是当前py文件 # python moduletest2_1.py -- 作用域只是当前的python # 获取当前文件的目录 # curpath = os.path.abspath(os.path.dirname(__file__)) # 获取项目根目录 D:\Projects\python\vscode\python-module projectpath = os.path.abspath(os.path.dirname(__file__)+r'../../../') # 1. 在该文件中添加 sys.path.append('项目根目录') -- 作用域只是当前py文件 sys.path.append(projectpath) # 很明显用这个路径'D:\Projects\python\vscode\python-module\src\main\package2' from module2 import fun2 from package3.module3 import fun3 # 很明显用这个路径 D:\Projects\python\vscode\python-module from src.main.package1.module1 import fun1 if __name__ == '__main__': print(sys.path) fun2() fun3() fun1()
打开cmd,切换到该文件所在目录,执行
python moduletest1.py
结果如下所示:
真会玩!!!
4、我们再引入模块module1.py和module2.py和module3.py
moduletest2_2.py如下
import sys import os # 测试第二种: # 在文件中添加 sys.path.append('项目根目录'), sys.path.append('当前文件所在的目录') -- 作用域只是当前py文件 # python moduletest2_2.py -- 作用域只是当前的python # 获取当前文件的目录 # curpath = os.path.abspath(os.path.dirname(__file__)) # 获取项目的main目录 D:\Projects\python\vscode\python-module\src\main mainpath = os.path.abspath(os.path.dirname(__file__)+r'../') # 1. 在该文件中添加 sys.path.append('项目根main目录') -- 作用域只是当前py文件 sys.path.append(mainpath) from package2.module2 import fun2 from package2.package3.module3 import fun3 from package1.module1 import fun1 if __name__ == '__main__': print(sys.path) fun2() fun3() fun1()
打开cmd,切换到该文件所在目录,执行
python moduletest1.py
结果如下所示:
是不是类似java的包,好会玩,快被玩坏了!!!
三、在命令窗口设置 PYTHONPATH
当你导入一个模块,Python 解析器对模块位置的搜索顺序是:
- 1、当前目录
- 2、如果不在当前目录,Python 则搜索在 shell 变量 PYTHONPATH 下的每个目录。
- 3、如果都找不到,Python会察看默认路径。UNIX下,默认路径一般为/usr/local/lib/python/。
模块搜索路径存储在 system 模块的 sys.path 变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。
这里我们就用到第2种搜索
1、按照惯例,先看一下sys.path
moduletest3.py如下
import sys if __name__ == '__main__': print(sys.path)
打开cmd,切换到该文件所在目录,执行
python moduletest1.py
结果如下所示:
2、在命令行设置PYTHONPATH -- 作用域是该窗口,只要窗口不关闭,在窗口中执行就行
moduletest3.py如下
import sys
if __name__ == '__main__':
print(sys.path)
打开cmd,执行
set PYTHONPATH=D:Projectspythonvscodepython-module
echo %PYTHONPATH%
结果如下所示:
切换到该文件所在目录,再次执行
python moduletest1.py
结果如下所示:
在该环境变量中:
自动添加的:'D:\Projects\python\vscode\python-module\src\main\package2'
我们添加的:'D:\Projects\python\vscode\python-module'
由此我们可以添加'D:\Projects\python\vscode\python-module'目录下的模块,需要引入相关包。
这里的作用域是该窗口。
3、引入模块module1.py和module2.py和module3.py
moduletest3.py如下
import sys # 测试第三种:在cmd中切换到该目录下,执行 set PYTHONPATH=D:\Projects\python\vscode\python-module # 我们可以查看 echo %PYTHONPATH% # 作用域是该cmd窗口,这要窗口下执行,都是有该环境变量存在 # python moduletest3.py from src.main.package2.module2 import fun2 from src.main.package2.package3.module3 import fun3 from src.main.package1.module1 import fun1 if __name__ == '__main__': print(sys.path) fun2() fun3() fun1()
在刚才窗口中执行
python moduletest1.py
结果如下所示:
如果窗口没有设置 PYTHONPATH,肯定会报错。
什么?你不信!!!
那我们就测试一下
重新打开一个窗口或者在该窗口执行以下操作:
set PYTHONPATH=
结果如下:
在刚才窗口中执行
python moduletest1.py
结果如下所示:
我就说嘛,请不要抬杠!!!
4、如果是在pycharm中,则不会报错,不需要在命令行设置PYTHONPATH,因为pycharm已经设置好了
注意!!!好玩的又来了。
有些会问,我没有设置PYTHONPATH,为什么在pycharm没有报错,那是因为pycharm已经设置好了。
pycharm就是采用类似这种的策略。
1)我们打开pycharm,导入该项目,看一下
打开 File --> Settings --> Build,Execution,Deployment --> Console --> Python Console
我们看一下启动脚本 Starting script
import sys; print('Python %s on %s' % (sys.version, sys.platform)) sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS])
好家伙!!!
启动脚本肯定是先执行的。 -- 至于如何设计,不太清楚,你们研究好了可以告诉我一声,我谢谢啊。
(1) 这里面先引入 import sys;
(2) 之后调用print('Python %s on %s' % (sys.version, sys.platform))打印信息,
我的打印结果是
也就是说
sys.version: 3.8.3 (default, Jul 2 2020, 17:30:36) [MSC v.1916 64 bit (AMD64)]
sys.platform: win32
(3) 然后是 sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS]),把目录添加到环境变量中,
至于WORKING_DIR_AND_PYTHON_PATHS是识别不了的,pycharm肯定在其他地方定义了。
不过幸亏这名字取得我们能看懂意思,就是就是工作目录和python目录。
这里的工作目录一看就是项目目录。我们可以验证一下。
2)我们已经把项目导入了pycharm,我们执行 moduletest3.py 试试看
当然我们这里什么都没设置
moduletest3.py如下:-- 和之前一样
import sys # 测试第三种:在cmd中切换到该目录下,执行 set PYTHONPATH=D:\Projects\python\vscode\python-module # 我们可以查看 echo %PYTHONPATH% # 作用域是该cmd窗口,这要窗口下执行,都是有该环境变量存在 # python moduletest3.py from src.main.package2.module2 import fun2 from src.main.package2.package3.module3 import fun3 from src.main.package1.module1 import fun1 if __name__ == '__main__': print(sys.path) fun2() fun3() fun1()
我们直接点pycharm的执行
结果如下:
我们把结果的内容复制下来:
D:ProgramFilesAnaconda3python.exe D:/Projects/python/vscode/python-module/src/main/package2/moduletest3.py ['D:\Projects\python\vscode\python-module\src\main\package2', 'D:\Projects\python\vscode\python-module', 'D:\ProgramFiles\JetBrains\PyCharm 2020.1\plugins\python\helpers\pycharm_display', 'D:\ProgramFiles\Anaconda3\python38.zip', 'D:\ProgramFiles\Anaconda3\DLLs', 'D:\ProgramFiles\Anaconda3\lib', 'D:\ProgramFiles\Anaconda3', 'D:\ProgramFiles\Anaconda3\lib\site-packages', 'D:\ProgramFiles\Anaconda3\lib\site-packages\win32', 'D:\ProgramFiles\Anaconda3\lib\site-packages\win32\lib', 'D:\ProgramFiles\Anaconda3\lib\site-packages\Pythonwin', 'D:\ProgramFiles\JetBrains\PyCharm 2020.1\plugins\python\helpers\pycharm_matplotlib_backend'] 我是来自 module2 我是来自 module3 我是来自 module1 Process finished with exit code 0
我的天,sys.path 中包含
'D:\Projects\python\vscode\python-module\src\main\package2', 'D:\Projects\python\vscode\python-module'
所以嘛,不要抬杠!!!
四、设置全局变量 -- 作用域是一直存在
1、添加环境变量
2、按照惯例,我们测试一下sys.path
moduletest4.py如下
import sys # 测试第四种:设置全局变量 -- 作用域是一直存在 # 对于Windows,右键我的电脑 --> 属性 --> 高级系统设置 --> 环境变量 --> 既可以添加用户环境变量,也可以添加系统环境变量 --> # 新建 --> 添加变量名:PYTHONPATH,添加变量值:D:Projectspythonvscodepython-module --> 确定 # python moduletest4.py if __name__ == '__main__': print(sys.path)
打开新的cmd(老的还是老配置,只有新的),切换到该文件所在目录,执行
python moduletest1.py
结果如下所示:
sys.path 中包含
'D:\Projects\python\vscode\python-module\src\main\package2',
'D:\Projects\python\vscode\python-module'
3、我们直接引入模块module1.py和module2.py和module3.py
moduletest4.py如下
import sys # 测试第四种:设置全局变量 -- 作用域是一直存在 # 对于Windows,右键我的电脑 --> 属性 --> 高级系统设置 --> 环境变量 --> 既可以添加用户环境变量,也可以添加系统环境变量 --> # 新建 --> 添加变量名:PYTHONPATH,添加变量值:D:Projectspythonvscodepython-module --> 确定 # python moduletest4.py from src.main.package2.module2 import fun2 from src.main.package2.package3.module3 import fun3 from src.main.package1.module1 import fun1 if __name__ == '__main__': print(sys.path) fun2() fun3() fun1()
打开新的cmd,切换到该文件所在目录,执行
python moduletest1.py
结果如下所示:
五、把文件添加到默认路径中 -- 还是算了吧,一般都不用这种的。
除非是针对python的封装等,比如Anaconda等。请自行解决。
当你导入一个模块,Python 解析器对模块位置的搜索顺序是:
- 1、当前目录
- 2、如果不在当前目录,Python 则搜索在 shell 变量 PYTHONPATH 下的每个目录。
- 3、如果都找不到,Python会察看默认路径。UNIX下,默认路径一般为/usr/local/lib/python/。
模块搜索路径存储在 system 模块的 sys.path 变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。
总结:
1、按照自动搜索方法,会先搜索当前目录,但是该方法的缺点是不能识别上级目录的模块,并且作用域只是该py文件
2、自己在py中添加环境变量,会识别本目录,以及从添加目录开始的模块,但是该方法的缺点是每个py文件都要添加环境变量,并且作用域只是该py文件
3、打开cmd,设置本窗口的变量。 set PYTHONPATH=D:\Projects\python\vscode\python-module,可以通过 echo %PYTHONPATH% 查看。这么设置的话,作用域是本窗口。
4、设置全局的环境变量。作用域一直存在。我们不需要了在删除之。
5、把文件添加到默认路径中。
参考:Python 模块 - https://www.runoob.com/python/python-modules.html