2013-07-12 7 views
25

は考えてみましょう:それはカウンター直感的なようだモジュール全体、のではなく、唯一のディスパッチ機能をインポートするにはかなり時間がかかりますモジュール全体から機能をインポートするのに時間がかかるのはなぜですか?

>>> timeit.timeit('from win32com.client import Dispatch', number=100000) 
0.18883283882571789 
>>> timeit.timeit('import win32com.client', number=100000) 
0.1275979248277963 

。誰かがなぜ単一の機能を使うためのオーバーヘッドが悪いのかを説明できますか?ありがとう!

答えて

33

ためです:

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を複数回使用している場合、それはだたとえば、独自の利点があります:

from win32com.client import Dispatch 

は同等ですそれを変数に代入する方が良いので、検索回数を減らすことができます。それ以外の場合は、win32com.client.Dispatch()を呼び出すたびに、win32comの検索が行われ、の中にはclient、最後には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から

:効率の理由から

注、各モジュールは一度だけ インタプリタセッションごとにインポートされます。したがって、モジュールを変更する場合は、 インタプリタを再起動する必要があります。つまり、対話的に をテストするモジュールが1つだけの場合は、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 
+1

私はこの答えが気に入っています! –

+3

それはすばらしい答えです:*コンセプト*、*実用的な*、*文書標準*を含みます。誰かのような最善の答えを得ることができます。 –

+0

包括的な説明、ありがとう! – TheoretiCAL

11

あなたが望む名前を取得するには、モジュール全体をインポートする必要があります... OSがモジュールをキャッシュしているため、後で.pycファイルへのアクセスが高速になります。

+0

各モジュールのモジュールを削除することで、再インポートを強制できます。 – TheoretiCAL

+0

私は質問をフォローアップすると思いますが、モジュール全体をキャッシュすることはできますが、Pythonもモジュールから関数をキャッチしていますか? – TheoretiCAL

+0

@JonClements:解析されるだけでなくインポートされます。 'from foo import bar'の後に' sys.modules ['foo'] 'を見ると' import foo'をしたのと全く同じことが分かります。 – abarnert

2

ここでの主な問題は、コードがタイミングと思われるタイミングをとらないことです。 timieit.timeit()はループ内でimportステートメントを100000回実行しますが、たいてい最初の反復では実際にインポートが実行されます。他のすべての繰り返しはモジュールをsys.modulesで検索し、モジュールのグローバル内で名前Dispatchを検索し、インポートするモジュールのグローバルにこの名前を追加します。基本的には辞書操作だけであり、バイトコードの小さな変化は目に見えるようになります。なぜなら、非常に安い辞書操作と比較して相対的な影響が大きいからです。

一方、実際にモジュールをインポートするのにかかる時間を測定すると、この2つの方法の違いはありません。どちらの場合も、今回は実際のインポートが完全に支配的です。名前の辞書を使いこなす違いが無視できなくなります。

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 
+0

私はちょうど最初のインポートが実際にインポートを実行することを認識し、私の質問の例をやや誤解を招くようにしています。その時代の結果がどのようにほぼ同じであるかを指摘してくれてありがとう。 – TheoretiCAL

関連する問題