我们在Python中,总是使用import来导入另外一个模块(文件)中的内容,如果是从Java或C转过来的程序员,有几个常见的坑要注意一下:
import也是执行语句,可以在代码任何部位执行。如果我们把import写在代码中间,IDE很可能会有个警告,但只要逻辑正确,就不用理它:
import a if __name__ == "__main__": import b b.methodx()
类似以上代码,只有当前文件作为主模块运行的时候,才导入模块b,不但减少了浪费,而且有可能避免循环引用。
- import执行的时候,如果是第一次导入,那么会把对应模块执行一遍,这个模块里所有的顶级代码都会执行,所以是个很耗时的操作,这点跟Java或者C只是获取类型定义完全不同;所以那些被复用的模块,要尽量减少在顶级代码块中实现逻辑。
即使在同一个模块里,代码定义的顺序也很重要,被引用的代码一定要在引用的前面定义,比如:
class A: x: int = 5 class B: def __init__(self, y: A): self.y = y if __name__ == "__main__": a = A() b = B(a) print(b.y.x)
上面这段代码里就不能把class A的定义放在class B后面,否则运行时会出错,提示
name 'A' is not defined
;当然如果仅仅是annotation里引用的话,可以通过from __future__ import annotations
来解决,这是另外一个问题了。
下面的内容是有表述错误的,但这个错误有些典型,还是保留下来,最下方会解释:
- import的namespace也很重要,命名空间不同,会被认为是不同的模块。而一个模块,
用绝对路径或者相对路径引入,会被识别为不同的命名空间,比如下面的package结构:
module_a的内容如下:
a = {"value": 15}
print(f'a={a} in module_a')
module_b会用绝对路径引用module_a
from lang.test_import.module_a import a
a['value'] = a['value'] + 1
print(f'a={a} in module_b')
module_c1和module_c2分别用相对路径和绝对路径引入module_a,再引入module_b,就会得到不同的结果:
from module_a import a
import module_b
print(f'a={a} in module_c1')
a={'value': 15} in module_a
a={'value': 15} in module_a
a={'value': 16} in module_b
a={'value': 15} in module_c1
from lang.test_import.module_a import a
import module_b
print(f'a={a} in module_c2')
a={'value': 15} in module_a
a={'value': 16} in module_b
a={'value': 16} in module_c2
可以看到在module_c1中,module_a被引入了两次,认作不同的模块,从而也引入了两个变量a,而c1模块,只承认自己引入的相对路径命名空间中的a,所以这里的a.value,并没有被模块b更改。
上面的表述中,关键错误在于:把同一路径下的绝对路径当做了相对路径,真正的相对路径引入,必须是.或者..开头的,而上面之所以写作from module_a import a
的形式,是因为直接运行python文件时,解释器找不到根package环境,只能把自己当做根;而如果它是被其他模块
import的,就会基于那个模块的根来判断命名空间;
如果一定要基于main方法运行,可以强行指定所属package:
__package__ = lang.test_import
from .module_a import a
import module_b
print(f'a={a} in module_c1')
这时的输出结果,就跟绝对路径一样了:
a={'value': 15} in module_a
a={'value': 16} in module_b
a={'value': 16} in module_c2
- 关于import和相对路径的含义和解释,我见到最清晰的中文文档是这篇:Python之模块详细梳理(四),关于相对导入报错最优雅解决方法
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。