Python中的局部导入语句
我觉得把导入语句放在使用它的代码片段旁边,可以让代码更容易读懂,因为这样可以更清楚地看到它依赖了哪些东西。Python会缓存这个吗?我需要在意这个吗?这样做是个坏主意吗?
def Process():
import StringIO
file_handle=StringIO.StringIO('hello world')
#do more stuff
for i in xrange(10): Process()
再多说一点:这是因为有些方法使用了库里比较复杂的部分,但当我把这个方法重构到另一个文件时,我往往没意识到漏掉了外部依赖,直到出现运行时错误才发现。
7 个回答
请查看 PEP 8:
导入的内容总是放在文件的最上面,紧接着模块的注释和文档字符串之后,然后再是模块的全局变量和常量。
请注意,这完全是一个风格上的选择,因为无论你在源文件的哪个地方写 import
语句,Python 都会一样对待它们。不过,我还是建议你遵循这个常见的做法,这样会让其他人更容易阅读你的代码。
抛开风格不谈,确实一个导入的模块只会被导入一次(除非你对这个模块调用了 reload
)。不过,每次调用 import Foo
时,系统会自动检查这个模块是否已经被加载过(通过查看 sys.modules
)。
再考虑一下两个功能相同的函数,其中一个尝试导入模块,而另一个则不导入:
>>> def Foo():
... import random
... return random.randint(1,100)
...
>>> dis.dis(Foo)
2 0 LOAD_CONST 1 (-1)
3 LOAD_CONST 0 (None)
6 IMPORT_NAME 0 (random)
9 STORE_FAST 0 (random)
3 12 LOAD_FAST 0 (random)
15 LOAD_ATTR 1 (randint)
18 LOAD_CONST 2 (1)
21 LOAD_CONST 3 (100)
24 CALL_FUNCTION 2
27 RETURN_VALUE
>>> def Bar():
... return random.randint(1,100)
...
>>> dis.dis(Bar)
2 0 LOAD_GLOBAL 0 (random)
3 LOAD_ATTR 1 (randint)
6 LOAD_CONST 1 (1)
9 LOAD_CONST 2 (100)
12 CALL_FUNCTION 2
15 RETURN_VALUE
我不太确定字节码在虚拟机中会被翻译成多少,但如果这是你程序中一个重要的循环,你肯定会更倾向于使用 Bar
的方式,而不是 Foo
的方式。
一个简单粗暴的 timeit
测试确实显示使用 Bar
的速度有了适度的提升:
$ python -m timeit -s "from a import Foo,Bar" -n 200000 "Foo()"
200000 loops, best of 3: 10.3 usec per loop
$ python -m timeit -s "from a import Foo,Bar" -n 200000 "Bar()"
200000 loops, best of 3: 6.45 usec per loop
其他回答对import
的工作原理有些混淆。
这条语句:
import foo
大致上等同于这条语句:
foo = __import__('foo', globals(), locals(), [], -1)
也就是说,它在当前的作用域中创建了一个与请求的模块同名的变量,并将调用__import__()
函数的结果赋值给它,这个函数使用了模块名和一堆默认参数。
__import__()
这个函数的作用是把一个字符串(比如'foo'
)转换成一个模块对象。模块会被缓存到sys.modules
中,而__import__()
首先会在这个地方查找——如果sys.modules
里有'foo'
的条目,那么__import__('foo')
就会返回那个条目,不管它是什么。它其实并不关心类型。你可以自己试试,运行以下代码:
import sys
sys.modules['boop'] = (1, 2, 3)
import boop
print boop
暂时不考虑风格问题,在函数内部使用导入语句是按你想要的方式工作的。如果这个模块之前从未被导入过,它会被导入并缓存到sys.modules
中。然后,它会把这个模块赋值给那个名字的局部变量。它绝对不会修改任何模块级的状态。它可能会修改一些全局状态(比如在sys.modules
中添加一个新条目)。
不过,我几乎从不在函数内部使用import
。如果导入模块会明显拖慢你的程序——比如它在静态初始化时进行长时间的计算,或者它是一个非常大的模块——而你的程序很少需要用到这个模块,那么只在需要的函数内部导入是完全可以的。(如果这样做不合适,Guido肯定会跳进他的时间机器,修改Python来阻止我们这么做。)但一般来说,我和大多数Python社区的人都把所有的导入语句放在模块的顶部,也就是模块作用域中。