python import - 模块导入概念及常见错误

1.几个概念

python模块(Module):

以.py结尾的python文件,其中的成员可以包括“变量、函数、类 ”等等。

python包(package):

简单来说,包就是文件夹,但该文件夹下一般存在 __init__.py 文件(python3不严格要求,但强烈建议), 该文件的内容可以为空。__init__.py 用于标识当前文件夹是一个包,当一个包被import时,首先会自动加载它的__init__.py文件。

搜索路径:

当你导入一个模块,Python 解析器对模块位置的搜索顺序是:

1、当前目录

2、如果不在当前目录,Python 则搜索在 shell 变量 PYTHONPATH 下的每个目录

3、如果都找不到,Python会察看默认路径。UNIX下,默认路径一般为/usr/local/lib/pythonx.x/… ,anaconda安装一般为/xxx/anacondax/pythonx.x/lib/… 。

你可以通过sys.path变量看到上述所有路径(顺序也入上所诉),代码如下:。

# test.py

import sys

print(sys.path)

 

输出:(应该为整个长list,这边为了方便区分做了写修改。)

2.两个语法

import [module | package] :

import语句用来载入模块,使用这种语法格式的 import 语句,会导入指定模块中的所有成员(包括变量、函数、类等)。当需要使用模块中的成员时,需用该模块名作为前缀,否则 Python 解释器会报错。

示例1:

python import - 模块导入概念及常见错误_第1张图片

其中PIL是包,Image是模块。Import 模块(Image)后,需要使用其中的函数(new)还是需要写上完整的前缀(PIL.Image.new)

示例2:

python import - 模块导入概念及常见错误_第2张图片

包(PIL)可也被直接import,但是如果只是导入一个包而不指名任何模块,且包中的__init__.py没有任何初始化操作,那么并不可以使用包内的模块及模块内的函数。

示例3:

python import - 模块导入概念及常见错误_第3张图片

import语句不能直接引用模块内的成员。

from [module | package] import [member | module]

from … import … 可以从模块中引用它内部的成员(函数,变量等),此时可以不加模块的前缀直接使用。(建议使用某模块少量成员时使用此方法)

from … import … 可以从包中引用模块,此时可以直接使用”模块名.成员名“来调用模块内的所有成员。

示例1:

其中PIL.Image 是模块,new是模块内的函数,new可以不加模块前缀(Image)直接使用。

示例2:

其中PIL是包,Image是模块。引用后可以使用”模块名.成员名“来调用模块内的所有成员。

3.绝对导入与相对导入

目录结构:

mypackage
├── __init__.py
├── module_bar.py
└── module_foo.py

从module_bar导入module_foo的几种方式:

# 方法一:

 import module_foo

# 方法二:

# 如果是上层文件夹写.., 上上层文件夹写..., 以此类推

from .  import module_foo

# 方法三:

from mypackage import module_foo
import mypackage.module_foo

其中:

对于python3而言,方法二是相对导入,方法一三是绝对导入。(python强烈不推荐相对导入)

4.直接运行与模块运行

目录:

.

└── mypackage

    ├── __init__.py

    └── module_foo.py

模块module_foo.py内容:

import sys
print(sys.path)

直接运行

python3 mypackage/module_foo.py   
['/xxx/MyProject/mypackage', xxxx, xxx, xxxx]

可以看出,当搜索路径中的“1、当前目录”(见第一节)就是模块所以在的目录。

模块运行

python3 -m mypackage.module_foo
['/xxx/MyProject', xxxx, xxx, xxxx]

可以看出,当搜索路径中的“1、当前目录”(见第一节)就是你的命令行所以在的目录。

5.常见的跨包调用错误

目录:

src
    ├── bar_package
    │   └── module_1.py
    ├── foo_package
    │   ├── module_2.py
    │   └── module_3.py
    └── main.py

模块module_3.py的内容:

from bar_package import module_1

问题:

在foo_package下,使用python module_3.py 就会出现“ No module named 'bar_package' ”错误。

分析:

入上节所说,直接运行的方式会将要运行模块所在目录放入搜索路径(此例中就是foo_package目录)。这使得搜索路径对 bar_package包不可见,所以会产生错误。

(推荐)解决1:

使用模块运行的方法,在scr目录下运行:

python3 -m foo_package.module_3

入上节所说,此时scr目录被加入到搜索路径当中,搜索路径对 bar_package包可见。

(不推荐)解决2:

修改模块内容,添加相对搜索路径:

import sys
sys.path.append("../")

print(sys.path)

from bar_package import module_1

此时在foo_package下运行“python3 module_3.py”可以成功。输出如下:

['/xxx/src/foo_package', 'xxx', 'xxx', '../' ]

从打印中可以看出搜索路径sys.path在最后添加了'../',这使得它能够找到bar_package包。

但是,在src下运行“python3  foo_package/module_3.py”还是显示“ No module named 'bar_package' ”错误。

这是因为添加的搜索路径是一个相对路径(“../”),在src下运行时,"../"指的是src的上层路径,此时也无法搜索到bar_package包。

(推荐)解决3:

修改模块内容,添加绝对搜索路径:

import sys
import os

sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))

print(sys.path)

from bar_package import module_1

此处用os.path.realpath(__file__)来获得文件所在的绝对路径。使用两个os.path.dirname()来代替相对路径中的“..”表示上层文件夹。

这时无论在何处使用直接运行方法运行module_3模块都能够正确地找到bar_package包。

参考链接

Python中import的用法 - 知乎 (zhihu.com)

Python 模块 | 菜鸟教程 (runoob.com)

Python导入模块,Python import用法(超级详细) (biancheng.net)

[Python]解决python3中关于import的疑难杂症 - SegmentFault 思否

你可能感兴趣的:(python,python)