1. 相关概念
1.1 import
import语句用来导入其他python文件(称为模块module),从而使用该模块里定义的类、方法或变量,从而达到代码复用的目的。
1.2 脚本 script
脚本通常是可以直接运行的代码,由其自身运行。
1.3 模块 module
通常,类、函数、变量存储在称成为模块(module)的.py文件中,可以隐藏代码实现的细节,将不同代码块重新组织,与主程序分离,简化主程序的逻辑,提高主程序的可读性;另外当需要更改具体细节代码时,只用更改模块代码,大量引用模块的主程序代码不用更改,方便程序更改优化。
script与module的重要区别:一个模块被当作模块导入时,文件名用作模块名,而如果将模块作为脚本执行,则模块名为 main:
脚本或模块的名称可以使用__name__属性来获得;
在模块代码中可以使用 if name == "main"来使模块中的代码有不同行为:
当模块被导入时,其__name__ != “main”,使 if name == "main"中的代码不被执行;
当模块被当作脚本程序直接运行时,name == “main”,使 if name == "main"中代码被执行;
另外,当模块被导入时,若不加 if name == “main”,模块中除类,函数等代码会自动被运行。
模块的说明文档以注释的形式放在.py文件的开头,使用模块的__doc__属性访问说明文档,格式如下:
"""
模块说明文档
"""
print(xxx.__doc__)
1.4 包 package
包含模块文件和__init__.py文件的文件夹,可以理解为一组模块的容器。
python 3.3以后可以不包含__init__,py文件,但最好还是设置,init.py文件可以为空,用来标识所在文件夹是一个包;
__init__.py文件本身是一个模块,其名字不是__init__.py,而是所在包的名字;
__init__.py文件为空时 仅仅用 import Package 什么都做不了,在__init__.py文件可以放入一些python初始化代码,当包被 import 时,__init__.py文件中的代码被会自动执行:
- 批量导入我们在此包中需要用到的模块,这样在使用时不用一一导入,方便实用
假如包名为Package1,包含Module1模块,Module1模块包含诸多类和函数,例如有class Add等等,
__init__.py文件也可以加上如下代码:
from Package1.Module1 import *
当另外程序用语句
import Package1 #导入包Package1后
可以在后边直接用:
add = Package1.Add()
导入包内模块中的类
- 如使用 from Package import * 语句导入一个包的所有内容,需要在__init__.py文件加上:
all = [Module1.py,Module2.py],其中包Package中有Module1和Module2文件
1.5 库 library
库是一个完整的文件系统,可以认为它是一个完整的项目打包,可直接调用或者运行,一个库可能包含多个包。
小结:
- 数据封装在容器(list、tuple、dict、str)里
- 代码封装在function里
- function和data封装在class里
以上三类都可以打包在module里- 多个module可以打包在package里
- 多个package可以打包在library里
2. import机制
示例文件结构
便于解释说明,建立如下结构的文件系统:
Tree
|____m1.py
|____m2.py
|____Branch
|____m3.py
|____m4.py
在m1.py写入:
import m2
from Branch import m3
m2.printSelf()
在m2.py写入:
def printSelf():
print("In m2")
在m3.py写入:
import m4
def printSelf():
print("In m3")
在m4.py写入:
def printSelf():
print(“In m4”)
2.1 方式一
上述在m1中导入m2是最常用的绝对导入,即在import 后直接写模块名
绝对导入:
绝对导入时python会在两个地方查找目标包或模块
- sys.path 列表中的路径,一般安装的python库的目录都在sys.path中,所以安装好的库可以直接import
- 运行文件所在的目录(此处运行文件时m1,目录是…\Tree)
但是最好不要用上述方法导入同目录下的文件(包或模块,以下统称文件)
2.2 方式二
上述m1中导入m3也属于绝对导入(在python3):
from package_name import module_name
python 以绝对导入方式中的两种路径查找目标包,然后导入目标包中的目标模块。
运行上述m1.py会报错,m1用from package_name import module_name的方式绝对导入m3没问题,但m3也是用绝对导入的方式绝对导入m4会出问题,因为m4和m1不在同一目录,在运行文件m1的目录会找不到m4。
当把上述m4迁移到m1的同一级目录中,运行就没问题了,m3导入m4时在运行文件m1的目录中可以找到m4
可见以上导入方式都是绝对导入,只会在两中路径查找目标文件:
- 针对sys.path路径,sys.path.append(“路径”),手动动态将目标文件的上一级路径加到sys.path肯定没问题
- 针对当前运行文件所在目录,当前运行文件只有一个,也即程序运行的入口文件,上例中就是m1,而m3是非运行入口文件
2.3 方式三
相对导入
对于非运行入口文件m3导入m4可以使用相对导入
from . import m4
def printSelf():
print("In m3")
# 此时运行是OK的
针对上边说的最好不要用import module_name 导入同目录下的文件问题,到这里就可以给出答案,因为类似m3导入m4这种情况,都是非运行入口文件,会出问题;
当用相对导入方式 from . import module_name就可忽略上述情况
- from . import module_name:导入和自己同目录的文件
- from .package_name import module_name:导入和自己同目录的包中的模块
- from … import module_name:导入上级目录的文件
- from …package_name import module_name:导入位于上级目录下的包的文件
2.4 方式四
上述运行入口文件m1导入m3用的绝对导入方式,也可以用相对导入
from .Branch import m3
m3.printSelf()
运行文件还可以使用命令行 -m 命令,用这种方式运行还会有其他import 导入问题,鉴于目前项目开发很少使用这种方式,因此有机会单独介绍。
总结
本文主要介绍了python中import语句的机制,主要分为绝对导入和相对导入。