本文不介绍import的用法和机制。仅介绍一个细节。
该细节需要和以下两点有联系:
# 1. 从一个模块中导入指定的类或者方法
form A import b
# 2. 从一个模块中导入全部
from A import *
第一个细节:
笔者之前根据这两个知识点,从其表面意思理解, 忽略了该细节。
该细节是:第二种方式,导入全部,是真的导入全部(当然除了if __name__ == '__main__'
里面的东西)。而第一种方式,确不仅仅只导入了指定的类和方法,还会执行在函数和类范围外代码。
举例说明:
有个名命为A的模块,内容如下
我们从另一个模块中import这个模块:
忽略这个细节之前,我会认为上图的代码运行后,只会输出:22222222222222222222222
但实际的输出是:
所以,可以总结:
form A import b这种方式,当该语句被执行时,会运行A模块中除了类和方法以及if __name__ == '__main__'
之外的所有代码。
这个细节,平时可能不会引出Bug,但在一些编码风格不好的程序中,就很容易埋雷。
因此,自省以下:
- 记住该知识点,当不幸爆雷时,可以很快定位问题。
- 最好不要在一个模块中除了类中和方法中编写具备业务功能的代码。
第二个细节
即`irom x import *中 * 号的作用。
可能大家都能记住的是,*号意味着将一个包里的所有模块全部导入,或者是将一个模块里的类和函数全部导入。
并且从编程风格上提倡不建议使用from x import *这种方式。
而这里需要记录的一个细节是,*号意味着可以直接使用目标函数的名字,而不用加前缀。
比如:
import time
time.sleep(1)
# 编程风格上不建议的方式
form time import *
sleep(1)
上面这个简单到不像话的例子,仅仅是为了说明这一点:通过*号,我们可以预先不知道time模块中有个命名为sleep的方法,当import 之后,即可以在运行时调用它。
总结一下:通过号,我们可以使得python在当运行时才知道方法名的一些方法得到调用,执行一些在运行前未知命名的方法。即调用程序都不知道它要调用的目标名字,则在编写程序时自然无法通过具体名字来导入。
以下示例简单介绍了这种使用场景:
先看代码目录:
lib 是一个包,main.py是程序执行文件。
lib包中有个test_lib.py模块,代码如下:
# lib/test_lib.py
def test_a():
print("test 11111111111111111111111111111")
def test_b():
print("test 22222222222222222222222222222")
lib包中有个__init__.py文件,代码如下:
# lib/__init__.py
from .test_lib import *
因为这里不知道test_lib中有哪些函数或者类,且需要把所有函数和类全部导入。所以用的*号
main.py代码如下:
# main.py
from lib import *
def exec_func(execname):
eval(execname)
if __name__ == '__main__':
exec_func("test_a()")
exec_func("test_b()")
这里也用的*号,且只能用星号,因为运行时传入的肯定是单纯的函数名,不可能指出这个函数来自哪个模块。
运行结果如下:
test 11111111111111111111111111111
test 22222222222222222222222222222
Process finished with exit code 0
如果main.py中使用from lib import test_lib
,将报如下错误:
Traceback (most recent call last):
File "C:/Users/卟知导/PycharmProjects/pythonProject/main.py", line 8, in <module>
exec_func("test_a()")
File "C:/Users/卟知导/PycharmProjects/pythonProject/main.py", line 5, in exec_func
eval(execname)
File "<string>", line 1, in <module>
NameError: name 'test_a' is not defined
总结:
使用星号,虽然大多数情况下不建议,因为它这个把全部成员名字直接导入命名空间的特性确实会造成一些冲突,和程序上的不友好阅读。但存在即有道理,这种场景下,不得不使用星号。
**
Tips
**
看看from lib import test_lib 与 from lib import *的命名空间。
通过命名空间的值,当from test_lib import *时,我们可以直接使用‘test_a’来调用test_a函数