对于系统的包我们导入没有疑问
但是如果我们自己写的文件夹里面的python文件呢?
自己写的文件import时候会出现路径问题的疑惑
比如同目录下面的python文件被import 时候
和 其他目录下面的python文件被import时候
根据当前的python文件是否为执行文件
import的python文件的路径就是有区别的
1.先看看python如何定位到模块文件的
使用 imoort a 或者 from a import func
使用这种import 的时候是如何定位到模块 a的呢?
直接使用 import sys 的时候又是如何定位到sys这个模块的呢?
这个学过python的都知道因为搜索路径存在。默认先在当前目录下搜索,
然后是python的安装目录下的文件
1.sys.path提供了搜索路径
如果 import sys
使用 sys.path 是可以看到有一些目录的
python按照这个目录顺序进行搜索。
2.文件的__name__提供了模块名字
每一个Python对象有这个__name__属性
如果当前的python为执行文件,此处简称为驱动文件,那么__name__的值就是__main__
可以理解为main函数
如果当前python不是执行文件,只是我们组织模块用的,那么__name__就是该文件的相对路径
3.python文件的定位就是 sys.path 和 __name__指定的路径的拼接
2.看看自己写的文件import的问题
下面做个试验看看这个自己写的文件import的问题
下面是做试验用的文件目录
folder1 目录下 存放 a.py 和 b.py
folder2 目录下 存放 c.py 和d.py
每个文件里面简单写几行代码:
#a.py
def f1():
print("this is function f1 in a.py")
print("a的__name__属性是:"+__name__)
#b.py
def f2():
print("this is function f2 in b.py")
print("b的__name__属性是:"+__name__)
#c.py
def f3():
print("this is function f3 in c.py")
print("c的__name__属性是:"+__name__)
#d.py
def f4():
print("this is function f4 in d.py")
print("d的__name__属性是:"+__name__)
试验1:同目录下的文件import --- a.py 为执行文件
在a.py 里面引入 文件b.py , 并且此时 a.py 为驱动文件,可以执行下a.py 看看结果
from b import f2
#a.py
def f1():
print("this is function f1 in a.py")
f2()
print("a的__name__属性是:"+__name__)
上面使用
from b import f2
这种很好理解,可以定位到模块 b
试验2:同目录下的文件import --- a.py 不执行
本次不执行 a.py , 我们弄一个demo.py 来作为驱动文件
然后在 demo.py 里面来 import a
注意下,此时 demo.py 引入 a.py 时候使用的是
from folder1.a
这里带着路径 folder1.a
# demo.py
from folder1.a import f1, f2
import sys
f1()
f2()
print(__name__)
然后运行下 demo.py:
居然报错了
有疑惑吧
刚才试验1 里面直接执行 a.py 没有错误啊
同样的代码,试验2中在demo.py里面执行,就错了,找不到模块 b 了
为什么呢?
试验3:在试验2基础上修改下a.py里面引入模块b 的路径
修改下 a.py
把 from b 修改为 from .b
加了一个.
from .b import f2
#a.py
def f1():
print("this is function f1 in a.py")
f2()
print("a的__name__属性是:"+__name__)
然后再运行下demo.py:
疑问:
为什么此时需要修改下 a.py 里面 模块b的imoport 路径才可以运行通过??
简单分析下:
对比下试验1 和试验2 其实还有区别的,
a.py 作为驱动文件和不作为驱动文件,还有驱动文件的目录发生了变化
试验1中:
a.py作为驱动文件,那么搜索路径就是当前文件夹 import理解/folder1
也就是搜索路径
那么b.py被引用,那么它的__name__就是 b (相对路径)
所以 2个组合起来是可以找到 b 的
试验2中:
此时demo.py作为驱动文件,那么当前目录 import理解/ 就是搜索路径
此时demo.py要找到 模块a , a被imort之后 ,那么a的__name__就是相对路径 folder1.a
根据前面所说搜索路径就是 sys.path 和当前模块__name__路径的拼接
所以demo.py加载 a 的时候实际上的搜索路径是 import理解/folder1
加载a的时候,首先要去加载b
当前的搜索路径是 import理解/folder1
此时b被引用,所以 b的__name__是 folder1.b
继续拼接
可以看到 2个 如果直接组合在一起的话: import理解/folder1/folder1/b
有 2个 folder1的 这个路径是错误的
所以 试验2中 from b import f2 此时就无法找到 模块b 了
如何正确找到呢?
那么就需要修改下 import 的模块路径了
使用 from .b 之后 表示了加载当前目录下面的模块 b
当前目录就是 /import理解/folder1 ,从而可以找到
如果 from 后面不加模块路径,那就是直接按照 上面的路径拼接进行查找的
试验4:import 不同目录的文件--a.py作为驱动文件
先在 a.py 里面使用 folder2下面的 c
此时把 a.py作为驱动文件进行试验,所以先把 from .b import f2 得改回来
from b import f2
from folder2.c import f3
#a.py
def f1():
print("this is function f1 in a.py")
f2()
f3()
print("a的__name__属性是:"+__name__)
此时会报错误,找不到模块
根据前面结论 sys.path+模块__name__指定路径拼接
此时的搜索路径为:
import理解/folder1
模块c被引用,此时它的__name__是 folder2.c
那么拼接起来肯定无法在 folder1目录下来找到 folder2的
此刻有人想不是相对路径么,既然当前在 folder1下面,那么..就是上级目录
所以使用
from ..folder2.c import f3 试试
结果还是报错的
ValueError: attempted relative import beyond top-level package
说明 ..是行不通的 不能使用..作为相对目录去寻找
那该怎么办呢?
只要能保证搜索路径能找到这个模块那就能import的
因此如果c.py的目录也在这个搜索路径里面就是可以的
试验5:sys.path 来定位路径
基于上面的试验4代码,当前问题是搜索路径为 import理解/folder1
如何去寻找 import理解/folder2呢?
既然搜索路径是通过这个 sys.path来进行的
那么修改这个 sys.path呢?
比如我们把上层目录加入到 sys.path里面看看
sys.path.append("../")
from b import f2
import sys
sys.path.append("../")
from folder2.c import f3
#a.py
def f1():
print("this is function f1 in a.py")
f2()
f3()
print("a的__name__属性是:"+__name__)
果然出来了
所以可以通过修改 sys.path 的值增加搜索路径解决问题
此时使用sys.path 是可以使用 ..表示上层目录的
通过上面的试验可以看到搜索路径的理解很重要
试验6 : import 不同目录的文件--a.py 不执行
本次不借助sys.path的修改
此时的 a.py 如果不执行呢?
此时 先把 from b 要改回 from .b
下面这段代码使用 demo.py来作为驱动文件执行
from .b import f2
from folder2.c import f3
#a.py
def f1():
print("this is function f1 in a.py")
f2()
f3()
print("a的__name__属性是:"+__name__)
此时再次驱动 demo.py执行
# demo.py
from folder1.a import f1, f2
import sys
f1()
f2()
print(__name__)
可以看到 此时 a.py 中 使用
from folder2.c import f3
这个是可以正常加载的
原因就是因为 当前搜索路径变为了 import理解/
从而寻找模块c 的时候 import理解/foleder2 是可以正常找到的
可见,这个搜索路径多么重要
而且驱动文件所在的目录也很重要
最好把它放到顶层根目录下面
试验7:如果使用 import b 不是 from b 这种呢
继续使用demo.py作为驱动
然后我们在 a.py 里面 使用 import b的方式去加载 b
运行demo.py:
结果显而易见,肯定找不到 b 的
此时必须使用
import folder1.b
from .b import f2
from folder2.c import f3
import folder1.b
#a.py
def f1():
print("this is function f1 in a.py")
f2()
f3()
print("a的__name__属性是:"+__name__)
很明细使用import b 这种方式
就是直接的把搜索路径和当前的模块名字路径拼接起来的进行查找
总结
通过上面的试验可以看到,能否找到我们自己写的模块文件
搜索路径很重要的
而且还跟当前的 文件是否为 驱动文件有直接关系
如上面的 a.py
它作为驱动文件 时候 就可以直接 import b
但是如果是demo.py作为驱动文件的时候,a.py 里面必须是
import folder1.b
都是因为当前搜索路径捣的鬼
方法
1.可以使用 sys.path里面增加路径的方式
指定一些路径加入到里面,这样就可以搜索到我们自己写的文件
2.将驱动文件放到最顶层,比如这里的 demo.py
然后从最顶层的目录为基准,里面写的文件的路径都是相对于它的
3.使用包
如果我们使用包,那么把包都部署在lib下面这样lib本身在 sys.path 里面的
这样像 系统包 那样的使用方式使用,这样不用担心相对路径的问题了
不管那种写法,还是要明白搜索路径是如何找到文件模块的