python中import 模块的路径问题

对于系统的包我们导入没有疑问

但是如果我们自己写的文件夹里面的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 里面的

这样像 系统包 那样的使用方式使用,这样不用担心相对路径的问题了

不管那种写法,还是要明白搜索路径是如何找到文件模块的

来源:lambda-fk

物联沃分享整理
物联沃-IOTWORD物联网 » python中import 模块的路径问题

发表评论