Dus*_*etz 44 python python-import
我认为将import语句放在靠近使用它的片段上有助于通过使其依赖关系更加清晰来实现可读性.Python会缓存吗?我应该关心吗?这是一个坏主意吗?
def Process():
import StringIO
file_handle=StringIO.StringIO('hello world')
#do more stuff
for i in xrange(10): Process()
Run Code Online (Sandbox Code Playgroud)
更合理一点:它是用于使用库的神秘位的方法,但是当我将该方法重构为另一个文件时,我没有意识到我错过了外部依赖,直到我遇到运行时错误.
Lar*_*ngs 72
其他答案表明了如何import真正起作用的轻微混乱.
这个说法:
import foo
Run Code Online (Sandbox Code Playgroud)
大致相当于这个陈述:
foo = __import__('foo', globals(), locals(), [], -1)
Run Code Online (Sandbox Code Playgroud)
也就是说,它在当前作用域中创建一个与所请求模块同名的变量,并为其分配调用__import__()该模块名称和一大堆默认参数的结果.
的__import__()函数处理概念性转换的字符串('foo')到模块对象.模块被高速缓存sys.modules,这是第一个__import__()看起来 - 如果sys.modules有一个条目'foo',那__import__('foo')将返回什么,无论它是什么.它真的不关心类型.你可以自己看到这个; 尝试运行以下代码:
import sys
sys.modules['boop'] = (1, 2, 3)
import boop
print boop
Run Code Online (Sandbox Code Playgroud)
暂且不考虑风格问题,在函数内部使用import语句可以实现您的需求.如果之前从未导入过该模块,则会在sys.modules中导入并缓存该模块.然后它将模块分配给具有该名称的局部变量.它不是不可不修改任何模块级状态.它确实可能修改一些全局状态(添加新条目sys.modules中).
也就是说,我几乎从不import在函数内部使用.如果导入模块会在程序中产生明显的减速 - 就像它在静态初始化中执行一个长计算,或者它只是一个庞大的模块 - 并且你的程序实际上很少需要模块来做任何事情,只在里面导入它是完全没问题的.它使用的功能.(如果这很令人反感,Guido会跳进他的时间机器并改变Python以防止我们这样做.)但作为一项规则,我和一般的Python社区将所有的import语句放在模块范围的模块顶部.
And*_*are 12
请参阅PEP 8:
导入总是放在文件的顶部,就在任何模块注释和文档字符串之后,以及模块全局变量和常量之前.
请注意,这纯粹是一种风格选择,因为Python会将所有import语句都视为相同,无论它们在源文件中的声明位置如何.我仍然建议您遵循常规做法,因为这将使您的代码对其他人更具可读性.
Mar*_*off 10
除了风格之外,导入的模块确实只会导入一次(除非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
Run Code Online (Sandbox Code Playgroud)
我不确定为虚拟机翻译多少字节码,但如果这是你程序的一个重要的内循环,那么你当然希望对该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
Run Code Online (Sandbox Code Playgroud)
我做到了这一点,然后希望我没有.通常,如果我正在编写一个函数,并且该函数需要使用StringIO,我可以查看模块的顶部,看看它是否被导入,然后添加它,如果不是.
假设我不这样做; 假设我在我的函数中本地添加它.然后假设某个点我或其他人添加了许多其他使用的函数StringIO.该人将查看模块的顶部并添加import StringIO.现在,您的函数包含的代码不仅意外而且冗余.
此外,它违反了我认为非常重要的原则:不要直接修改函数内部的模块级状态.
编辑:
实际上,事实证明以上所有都是无稽之谈.
导入模块不会修改模块级状态(它初始化正在导入的模块,如果还没有其他任何东西,但这根本不是同样的事情).除了sys.modules在本地范围内查找和创建变量之外,导入已经在其他地方导入的模块不需要任何费用.
知道了这一点,我觉得有点愚蠢地修复我修复它的代码中的所有地方,但那是我的交叉.
| 归档时间: |
|
| 查看次数: |
29050 次 |
| 最近记录: |