Python中创建包和增添包的路径(sys.path.append())
作者:Python热爱者
python中创建自己的包(package),其实就只需要里创建一个文件夹就可以了。文件夹里不是必须要有是__init__.py这个文件的,不过如果你的文件夹里存在这个__init__.py文件,那么当你在import这个包时python会立刻执行__init__.py里的内容并且只会执行__init__.py。相反,如果你的文件夹(这个package)里没有这个文件,当import这个package时就不会做任何事情(换句话说你python只会load这个包的名称,但里面接着进行任何其他的操作,你可以查看这个包的默认属性)。因此一般情况下,一个package中是必须要有__init__.py
,并且常常会在__init__.py
文件中执行import语句,比如import进一些本文件夹里的一些包,或者封装好一些类,一些函数。这样做的好处是,只要你import了这个package,那么你就可以使用__init__.py
中load的所有包。
1、创造一个包
我们要做的就是两件事
在工作目录下创建一个文件夹命名为
My_package
在文件夹
My_package
中再创建一个__init__.py
文件
__init__.py
:
print('包加载成功')
我们可以是在My_package文件夹的同一级目录中加载import一下(注意工作目录要在的同一级目录中才能加载到My_package,后面会介绍如果不在同一级目录该怎么做)
import My_package #输出 包加载成功
可以看到加载包的时候(并且在My_package目录下自动生成了一个__pycache__
的文件夹),我们写在__init__.py
中的命令行被执行了接下来我们更加丰富一下__init__.py
的内容,给这个里面添加两个函数:
__init__.py
:
print('包加载成功') def fun1(a): print('执行函数1:',a) return def fun2(a): print('执行函数2:',a)
接下来我们导入这个包,并执行里面的函数。
import My_package My_package.fun1('开始执行') My_package.fun2('开始执行') #输出 包加载成功 执行函数1: 开始执行 执行函数2: 开始执行
可以看到这些函数都已经被执行了。
当然我们可以使用from My_package import *
这种方式来完成上面的内容
from My_package import * fun1('开始执行') fun2('开始执行') #输出 包加载成功 执行函数1: 开始执行 执行函数2: 开始执行
可以看到结果是一样的,由此我们已经创建自己的包了
2、为包增加模块
我们常见的包里面都会有很多很多其他的.py程序,并不是只有__init__.py
,因为有些内容不需要在导入的时候就执行。下面我们为My_package增加两个模块。model1.py和model2.py。
model1.py
print('这里是model1') def fun1(): print('这里是model1的第一个函数') return def fun2(): print('这里是model1的第二个函数') return
model2.py
print('这里是model2') def fun1(): print('这里是model2的第一个函数') return def fun2(): print('这里是model2的第二个函数') return
创建完这两个模块之后,我们My_package文件夹下就有四个文件了:
- 文件夹
__pycache__
- 文件
__init__.py
- 文件model1.py
- 文件model2.py
下面我们用不同的方式来导入包和模块,并执行。
方式一:import My_package
.模块名
import My_package.model1 import My_package.model2 #输出 包加载成功 这里是model1 这里是model2 My_package.model1.fun1() My_package.model2.fun2() #输出 这里是model1的第一个函数 这里是model2的第二个函数 My_package.fun1('OK') #输出 执行函数1: OK
可以看到__init__.py
依然被执行了的,而且在导入包的时候,不同的model中的代码也在import的时候执行了。需要注意的是,采用这种import 包名.模块名的方式,会出现一个全局变量My_package来指向这个包(我们可以通过dir(My_package)来查看这个全局变量的属性,我们可以看到’fun1’,‘fun2’,‘model1’,'model2’都出现了),因此我们可以采用 包名.模块名.函数名 的方式调用不同model的函数。
我们还可以通过My_package.函数名的方式来执行__init__.py
里的函数(这是因为采用import 包名.模块名的方式来载入,python依然会执行package中的__init__.py
,并且生成的对象会被命名为My_package)。
我们可以通过加as的方法来对这一串很长的包名来重新命名,这就是介绍的第二种方法。
方式二:import My_package.model1 as xxx
import My_package.model1 as new_name1 import My_package.model2 as new_name2 #输出 包加载成功 这里是model1 这里是model2 new_name1.fun1() new_name2.fun2() #输出 这里是model1的第一个函数 这里是model2的第二个函数
可以看到我们可以使用new_name1这个变量来代替My_package.model1。这种采用as进行重命名的方式需要注意的是,此时python中将不会出现叫My_package的全局变量来指向这个package了,因此程序会无法找到My_package这个变量。因为python会将最外层的这个model1赋值给new_name1
,而不再保存My_package的任何东西,尽管执行了My_package
中__init__.py
。
方法三:from My_package import model1
from My_package import model1 from My_package import model2 #输出 包加载成功 这里是model1 这里是model2 model1.fun1() model2.fun2() #输出 这里是model1的第一个函数 这里是model2的第二个函数 My_package.fun1('OK') #输出 NameError: name 'My_package' is not defined --------------------------------------------------------------------------- NameError Traceback (most recent call last) /tmp/ipykernel_9767/1183774725.py in <module> 2 from My_package import model2 3 ----> 4 My_package.fun1('OK') NameError: name 'My_package' is not defined
根据导入包的方式顾名思义,“从My_package中导入model1.py”,这个时候我们可以直接使用model1.函数名()来调用函数了。可以看到跟前一种方式一样此时python中将不会出现叫My_package的全局变量,虽然已经执行了__init__.py
,但python没有将My_package赋值给任何变量,因此无法找到My_package
。
方法四:from My_package import *
from My_package import * fun1('OK') #输出 包加载成功 执行函数1: OK modle1.fun1() #输出 NameError: name 'modle1' is not defined
这种方式加载其实就相当于直接把__init__.py
直接加载到工作区,直接可以使用__init__.py
中的类或者函数,不用再添加My_package.函数名。不过要注意的是这种导入,只导入了My_package中__init__.py
,并没有导入其他的model1,或者model2,因此第三行是会报错的(并且我们可以看到并没有任何model的打印出现,其实这也侧面说明了当只采用import package的方式导入包时,python只会执行__init__.py
)。
我们又该怎么调用model里的函数呢?用下面的方式就可以了。
from My_package.model1 import * #输出 包加载成功 这里是model1 fun1() fun2() #输出 这里是model1的第一个函数 这里是model1的第二个函数
其实不推荐大家采用这这种方法,因为如果在不同model中出现了相同的函数名方法,后导入的包将会把前者的函数名给掩盖。比如本文的这两个例子,model1和modle2的函数都叫做fun1,fun2,那如果我们按照这个方式同时导入这两个模块,会出现什么问题呢?
from My_package.model1 import * from My_package.model2 import * #输出 包加载成功 这里是model1 这里是model2 fun1() fun2() #输出 这里是model2的第一个函数 这里是model2的第二个函数
大家可以看到前两句执行之后,显示两个包都被导入了,但是我们执行fun1和fun2时编译器识别的是后导入的模块model2的fun1和fun2,而model2的函数被掩盖了,因此不推荐大家采用方法4作为导入包的方法。
3、包的路径问题
刚学python的时候我非常好奇,import的包到底在那里?我pip install xxx下载下来的包又在那里?(当然可以通过路径查找),为什么下载下来的包在不同的地方但是import却能找到这些散落到各地的包?其实这些问题都归结到import是如何导入到包的。其实import包的时候时,python会在一些路径中搜索是否存在名字相符的文件夹(package)或者.py文件。
大家可以通过运行:
import sys sys.path #输出 ['/home/g4/.vscode/extensions/ms-toolsai.jupyter-2021.3.684299474/pythonFiles/vscode_datascience_helpers/../.does-not-exist', '/home/g4/.vscode/extensions/ms-toolsai.jupyter-2021.3.684299474/pythonFiles', '/home/g4/.vscode/extensions/ms-toolsai.jupyter-2021.3.684299474/pythonFiles/lib/python',...] (在ubuntu系统上演示的)
查看的当前的系统环境环境变量(sys.path是一个列表,每个元素是路径),在import的时候python就会在这些路径中去寻找有没有符合的名字,如果没有找到,就会返回我们最常见的"ModuleNotFoundError: No module named ‘kae’".
通过这个问题的讨论其实我们可以知道,如果我们自己编写的包想要被import到,就需要把包的路径写道环境变量中(前文演示的时候工作目录是会自动加入到import的查询范围内的)。我们不用去修改电脑的环境变量,因为刚说道sys.path是一个列表,因此我们可以通过append方法加入我们包所在路径可以了。
为了演示,我把前面写好的My_package文件夹放到了工作目录的下一级目录。
import My_package #输出 ModuleNotFoundError: No module named 'My_pacackge' --------------------------------------------------------------------------- ModuleNotFoundError Traceback (most recent call last) /tmp/ipykernel_31481/1446587236.py in <module> ----> 1 import My_pacackge ModuleNotFoundError: No module named 'My_pacackge'
不出意外的,报错了。接下来我们添加My_package文件夹的目录到系统目录中
import sys sys.path.append('/home/g4/桌面/project') print('##############') import My_package #输出 ############## 包加载成功
可以看到,包被正常加载了。对于自己写的模块我们可以采用这种方式来对其进行加载。
4、相对路径导入包的问题
为了更好区分,我们将model2.py中的两个函数改一下名字。
#学习中遇到问题没人解答?小编创建了一个Python学习交流群:531509025 print('这里是model2') def model2_fun1(): print('这里是model2的第一个函数') return def model2_fun1fun2(): print('这里是model2的第二个函数') return
在同一个package中,如果model1.py中想使用model2.py的函数,那我们可以通过import My_package.model2的方式进行正常导入。不过会有个问题,如果某一天,我把文件夹My_package更名了,那么此时mode1.py中的import My_package.model2将会报错。如何避免这个问题呢,我们就可以采用相对路径的方式导入包。即将import My_package.model2改为import .model2的方式导入。
那么无论文件夹My_package如何更名,只要model1.py与model2.py是在同一个package下的,那么这行代码都会正确找到。
接下来我们要说一下原理:python相对路径导入包的方法其本质是,都是先找到绝对路径再import。他会根据model1的__package__
变量去找到model1的package是谁(本例中就是My_package),再将import .model2复原成import My_package.model2,从而完成导入。
明白这个原理之后,相信大家就会知道,为什么我直接运行model1.py时,其中的import .model2会报错了。因为当你直接运行model1.py的时候,这个文件将会当作main package 加载到内存中,此时它并不属于任何的的package,所以import .model2就不会转化成import My_package.model2,也就无法找到正确的model2,从而报错。
到此这篇关于Python中创建包和增添包的路径(sys.path.append())的文章就介绍到这了,更多相关Python创建包和增添包路径内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!