>>> timeit.timeit('from win32com.client import Dispatch', number=100000)
0.18883283882571789
>>> timeit.timeit('import win32com.client', number=100000)
0.1275979248277963
它需要更長的顯著僅導入調度功能,而不是整個模塊,這似乎與直覺相反。有人能解釋爲什麼採取單一功能的開銷太糟糕了嗎?謝謝!
>>> timeit.timeit('from win32com.client import Dispatch', number=100000)
0.18883283882571789
>>> timeit.timeit('import win32com.client', number=100000)
0.1275979248277963
它需要更長的顯著僅導入調度功能,而不是整個模塊,這似乎與直覺相反。有人能解釋爲什麼採取單一功能的開銷太糟糕了嗎?謝謝!
這是因爲:
from win32com.client import Dispatch
等同於:
import win32com.client #import the whole module first
Dispatch = win32com.client.Dispatch #assign the required attributes to global variables
del win32com #remove the reference to module object
但from win32com.client import Dispatch
有自身的優勢,例如,如果你在代碼中使用win32com.client.Dispatch
多次那麼它的最好把它分配給一個變量,這樣可以減少查找次數。否則,每次撥打win32com.client.Dispatch()
將首先搜索搜索win32com
,然後client
內部win32com
,最後Dispatch
內部win32com.client
。
字節碼的比較:
從字節代碼很顯然,對於from os.path import splitext
需要的步驟數比簡單import
越大。
>>> def func1():
from os.path import splitext
...
>>> def func2():
import os.path
...
>>> import dis
>>> dis.dis(func1)
2 0 LOAD_CONST 1 (-1)
3 LOAD_CONST 2 (('splitext',))
6 IMPORT_NAME 0 (os.path)
9 IMPORT_FROM 1 (splitext)
12 STORE_FAST 0 (splitext)
15 POP_TOP
16 LOAD_CONST 0 (None)
19 RETURN_VALUE
>>> dis.dis(func2)
2 0 LOAD_CONST 1 (-1)
3 LOAD_CONST 0 (None)
6 IMPORT_NAME 0 (os.path)
9 STORE_FAST 0 (os)
12 LOAD_CONST 0 (None)
15 RETURN_VALUE
模塊緩存:
注意from os.path import splitext
後,你仍然可以訪問使用sys.modules
因爲Python緩存導入的模塊的os
模塊。
從docs:
注爲了提高效率,每個模塊僅進口每 解釋器會話一次。因此,如果您更換模塊,則必須重新啓動解釋器 - 或者,如果它只是一個模塊,您希望以交互方式測試 ,請使用
reload()
(例如,reload(modulename)
。
演示:
import sys
from os.path import splitext
try:
print os
except NameError:
print "os not found"
try:
print os.path
except NameError:
print "os.path is not found"
print sys.modules['os']
輸出:
os not found
os.path is not found
<module 'os' from '/usr/lib/python2.7/os.pyc'>
時序比較:
$ python -m timeit -n 1 'from os.path import splitext'
1 loops, best of 3: 5.01 usec per loop
$ python -m timeit -n 1 'import os.path'
1 loops, best of 3: 4.05 usec per loop
$ python -m timeit -n 1 'from os import path'
1 loops, best of 3: 5.01 usec per loop
$ python -m timeit -n 1 'import os'
1 loops, best of 3: 2.86 usec per loop
整個模塊仍然需要導入才能從中獲得所需的名稱......您還會發現操作系統正在緩存模塊,因此後續訪問.pyc
文件的速度會更快。
忘掉.pyc文件加快,謝謝! – TheoretiCAL
我想跟進問題,整個模塊可以被緩存,但python是否也從模塊捕獲功能? – TheoretiCAL
@JonClements:不只是解析,而是導入。在'from foo import bar'後面,看看'sys.modules ['foo']',你會看到完全一樣的東西,就好像你已經完成了'import foo'一樣。 – abarnert
這裏的主要問題是,你的代碼不能計算你認爲它是計時的。 timieit.timeit()
將在循環中運行import
語句100000次,但最多第一次迭代實際上將執行導入。所有其他迭代只需查找sys.modules
中的模塊,在模塊的全局變量中查找名稱Dispatch
,並將此名稱添加到導入模塊的全局變量中。所以它基本上只是字典操作,並且字節碼中的小變化將變得可見,因爲與非常便宜的字典操作相比,相對影響很大。
另一方面,如果您測量實際導入模塊所花費的時間,則無法看到兩種方法之間的差異,因爲在這兩種情況下,此時間完全由實際導入爲主,並且與名字字典混淆的差異變得可以忽略不計。我們可以通過刪除sys.modules
模塊中的每個迭代迫使reimports:
In [1]: import sys
In [2]: %timeit from os import path; del sys.modules["os"]
1000 loops, best of 3: 248 us per loop
In [3]: %timeit import os.path; del sys.modules["os"]
1000 loops, best of 3: 248 us per loop
In [4]: %timeit from os import path
1000000 loops, best of 3: 706 ns per loop
In [5]: %timeit import os.path
1000000 loops, best of 3: 444 ns per loop
我剛剛意識到第一次導入只會實際執行導入,使我的問題中的例子有點誤導。感謝您指出在時間上的結果大致相同。 – TheoretiCAL
我更喜歡這個答案! –
它真棒答案 - 包括:*概念*,*實用證明*和* doc標準*。像任何人都能得到的最佳答案。 –
綜合解釋,謝謝! – TheoretiCAL