2013-07-12 15 views
25

xem xét:Tại sao phải mất nhiều thời gian hơn để nhập một hàm từ một mô-đun so với toàn bộ mô-đun chính nó?

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

Nó mất nhiều thời gian đáng kể để nhập khẩu chỉ có chức năng văn chứ không phải là toàn bộ mô-đun, mà dường như truy cập trực quan. Ai đó có thể giải thích lý do tại sao chi phí cho việc sử dụng một chức năng đơn giản là xấu? Cảm ơn!

Trả lời

33

Đó là bởi vì:

from win32com.client import Dispatch 

tương đương với:

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 

Nhưng from win32com.client import Dispatch có lợi thế riêng của mình, ví dụ nếu bạn đang sử dụng win32com.client.Dispatch nhiều lần trong mã của bạn thì đó là tốt hơn để gán nó cho một biến, do đó số lượng tra cứu có thể được giảm. Nếu không, mỗi cuộc gọi đến win32com.client.Dispatch() trước tiên sẽ tìm kiếm tìm kiếm win32com và sau đó client bên trong win32com và cuối cùng là Dispatch bên trong win32com.client.


Byte-code so sánh:

Từ mã byte rõ ràng là số bước cần thiết cho from os.path import splitext là lớn hơn đơn giản 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  

Mô-đun bộ nhớ đệm:

Lưu ý rằng sau khi from os.path import splitext bạn vẫn có thể truy cập vào os mô-đun sử dụng sys.modules vì cache python module nhập khẩu.

Từ docs:

Note Vì lý do hiệu quả, mỗi mô-đun chỉ được nhập khẩu một lần mỗi phiên dịch viên. Do đó, nếu bạn thay đổi mô-đun, bạn phải khởi động lại trình thông dịch - hoặc, nếu chỉ là một mô-đun bạn muốn thử nghiệm tương tác, hãy sử dụng reload(), ví dụ: reload(modulename).

Demo:

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'] 

đầu ra:

os not found 
os.path is not found 
<module 'os' from '/usr/lib/python2.7/os.pyc'> 

so sánh Thời gian:

$ 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

Tôi thích câu trả lời này tốt hơn! –

+3

Nó tuyệt vời một câu trả lời - bao gồm: * khái niệm *, * thực tế-proof *, và * doc-standard *. Câu trả lời hay nhất như bất cứ ai cũng có thể nhận được. –

+0

Giải thích toàn diện, cảm ơn! – TheoretiCAL

11

Toàn bộ mô-đun vẫn phải được nhập để lấy tên bạn muốn từ đó ... Bạn cũng sẽ thấy rằng hệ điều hành đang lưu vào bộ đệm để truy cập sau vào tệp .pyc sẽ nhanh hơn.

+0

Quên tốc độ .pyc lên, cảm ơn! – TheoretiCAL

+0

Tôi đoán theo dõi câu hỏi, toàn bộ mô-đun có thể được lưu trữ, nhưng không python cũng bắt chức năng từ các mô-đun? – TheoretiCAL

+0

@JonClements: Không chỉ được phân tích cú pháp, mà được nhập. Sau 'from foo import bar', hãy nhìn vào' sys.modules ['foo'] ', và bạn sẽ thấy chính xác điều tương tự như bạn đã làm' import foo'. – abarnert

2

Vấn đề chính ở đây là mã của bạn không phải là thời gian mà bạn cho rằng đó là thời gian. timieit.timeit() sẽ chạy câu lệnh import trong vòng lặp, 100000 lần, nhưng tối đa lần lặp đầu tiên sẽ thực sự thực hiện việc nhập. Tất cả các lần lặp lại khác chỉ đơn giản là tìm kiếm mô-đun trong sys.modules, tra cứu tên Dispatch trong các hình cầu của mô-đun và thêm tên này vào các hình cầu của mô-đun nhập. Vì vậy, về cơ bản nó chỉ là các hoạt động từ điển, và các biến thể nhỏ trong mã byte sẽ trở thành hiển thị vì có ảnh hưởng tương đối so với các hoạt động từ điển rất rẻ là lớn.

Nếu, mặt khác, bạn đo thời gian cần để nhập mô-đun, bạn không thể thấy sự khác biệt giữa hai phương pháp, vì trong cả hai trường hợp, thời gian này hoàn toàn bị chi phối bởi nhập thực tế sự khác biệt không quan trọng với từ điển tên trở nên cẩu thả. Chúng tôi có thể buộc tái tạo lại bằng cách xóa mô-đun từ sys.modules trong mỗi lần lặp lại:

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

Tôi vừa mới nhận ra lần nhập đầu tiên sẽ thực sự thực hiện quá trình nhập, làm cho ví dụ trong câu hỏi của tôi có phần sai lệch. Cảm ơn bạn đã chỉ ra cách kết quả trong thời gian gần như giống nhau. – TheoretiCAL

Các vấn đề liên quan