Python import辨析
一些概念:
- script 脚本
- module 模块
- package 包
- library 库
一、建立目录结构
Tree
|____ m1.py
|____ m2.py
|____ Branch
|____m3.py
|____m4.py
小例子:
# m1.py
import m2
m2.print_self()
# m2.py
def print_self():
print("m2")
终端上运行 python m1.py
输出:m2
说明此时import是没问题的。
小例子2:
# m1.py
import m2
m2.print_self()
# m2.py
def print_self():
print("m2")
print_self()
终端上运行 python m1.py
输出:
m2 m2
此时m2被输出了两次,这并不是我们想要的。
二、import语句的第一种用法
import module_name
即import后直接接模块名。
这种情况下,python会在两个路径下寻找模块。
sys.path
- 当前文件所在目录
前者可通过以下语句查看内容,其实sys.path
里就包含了当前文件所在目录。
import sys
print(sys.path)
小例子2的解决办法:
# m1.py
import m2
m2.print_self()
# m2.py
def print_self():
print("this is m2")
print(__name__)
if __name__ == '__main__':
print_self()
终端上运行 python m1.py
输出:
m2
this is m2
解释:在执行import时,被引入的模块的__name__
会变成文件名字,在此例中为m2。因此并不会执行判断语句内的语句。
__name__
为python的内置变量。可以用print(dir())查看内置变量。
小例子3:
# m1.py
import m3
m3.print_self()
# m3.py
def print_self():
print("m3")
终端上运行 python m1.py
输出:
Traceback (most recent call last):
File "/Users/xxx/Tree/m1.py", line 1, in <module>
import m3
ModuleNotFoundError: No module named 'm3'
解释:此时在sys.path中找不到m3模块,这是因为m3.py和m1.py不在同一目录下。
正确做法是将import语句改为import Branch.m3
。这属于绝对导入,后文会介绍。
# m1.py
import Branch.m3
Branch.m3.print_self()
三、import语句的第二种用法
from package_name import module_name
即从包中加载模块。一般把模块(module)组成的集合称为包(package)。
与第一种写法类似,Python会在sys.path
和运行文件目录
这两个地方寻找包,然后导入包中名为module_name的模块。
例子1:
# m1.py
from Branch import m3
m3.print_self()
# m3.py
def print_self():
print("m3")
终端上运行 python m1.py
输出:m3
说明此时from xx import xx是没问题的。
例子2:
在m4.py文件下
# m4.py
def print_self():
print("m4")
# m3.py
import m4
def print_self():
print("m3")
# m1.py
from Branch import m3
m3.print_self()
终端上运行 python m1.py
输出:
Traceback (most recent call last):
File "/Users/xxx/Tree/m1.py", line 1, in <module>
from Branch import m3
File "/Users/xxx/Tree/Branch/m3.py", line 1, in <module>
import m4
ModuleNotFoundError: No module named 'm4'
说明此时找不到m4模块,这是为什么呢?
一个解释:当前运行的是m1.py文件,sys.path和当前目录下都没有m4.py文件
四、绝对导入和相对导入
上面讲的import两种写法均属于绝对导入,即用于导入sys.path中的包和运行文件所在目录下的包。
对于sys.path中的包,这种写法毫无问题。但如果导入的是自己写的文件,如果是非运行入口文件(比如上面的m1.py是运行入口文件,可以使用绝对导入),则需要相对导入。
类似绝对路径和相对路径。例子2要想运行成功,得将m3.py改为
# m3.py
from . import m4
def print_self():
print("m3")
相对导入的写法:
from . import module_name
。导入和自己同目录下的模块。from .package_name import module_name
。导入和自己同目录的包的模块。from .. import module_name
。导入上级目录的模块。from ..package_name import module_name
。导入位于上级目录下的包的模块。
运行入口文件(m1.py)也可以使用相对导入,比如将m1.py的内容改为:
# m1.py
from .Branch import m3
m3.printSelf()
但此时不能用python m1.py
命令,而是需要进入到Tree所在的目录,使用python -m Tree.m1
来运行。
直接运行 ,会报错
Traceback (most recent call last):
File "/Users/xxx/Tree/m1.py", line 1, in <module>
from .Branch import m3
ImportError: attempted relative import with no known parent package
一个解释:尝试在没有已知父包的情况下进行相对导入。这是因为运行m1.py时__name__
变量为main
,找不到父包。然而,如果m1.py的文件是被引入的,那么m1.py的__name__
变量的值会包括父包路径。
五、import的其他用法
import moudle_name as alias
。
有些module_name比较长,之后写它时较为麻烦,或者module_name会出现名字冲突,可以用as来给它改名,如import numpy as np
。
from module_name import function_name, variable_name, class_name
。
上面导入的都是整个模块,有时候我们只想使用模块中的某些函数、某些变量、某些类,用这种写法就可以了。使用逗号可以导入模块中的多个元素。
- 有时候导入的元素很多,可以使用反斜杠来换行,官方推荐使用括号。
from Tkinter import Tk, Frame, Button, Entry, Canvas, Text, \
LEFT, DISABLED, NORMAL, RIDGE, END # 反斜杠换行
from Tkinter import (Tk, Frame, Button, Entry, Canvas, Text,
LEFT, DISABLED, NORMAL, RIDGE, END) # 括号换行(推荐)
Reference:
Python中import的用法 - 门书生的文章 - 知乎 https://zhuanlan.zhihu.com/p/63143493
【python import中的绝对引用和相对引用】 https://www.bilibili.com/video/BV1EK411g7Ff?share_source=copy_web&vd_source=f263eddd9c6fc88c2b8c3f4f1f660922
python中的模块、库、包有什么区别? - 风影忍着的回答 - 知乎 https://www.zhihu.com/question/30082392/answer/2030353759