Các báo cáo nhập khẩu phải luôn luôn ở đầu mô-đun?


403

PEP 08 tuyên bố:

Nhập khẩu luôn được đặt ở đầu tệp, ngay sau bất kỳ nhận xét và tài liệu mô-đun nào, và trước toàn cầu mô-đun và hằng số.

Tuy nhiên, nếu lớp / phương thức / hàm mà tôi đang nhập chỉ được sử dụng trong các trường hợp hiếm hoi, chắc chắn sẽ hiệu quả hơn khi thực hiện nhập khi cần?

Đây không phải là:

class SomeClass(object):

    def not_often_called(self)
        from datetime import datetime
        self.datetime = datetime.now()

hiệu quả hơn thế này?

from datetime import datetime

class SomeClass(object):

    def not_often_called(self)
        self.datetime = datetime.now()

Câu trả lời:


283

Nhập mô-đun khá nhanh, nhưng không phải là ngay lập tức. Điều này có nghĩa rằng:

  • Đặt hàng nhập khẩu ở đầu mô-đun là tốt, bởi vì đó là một chi phí không đáng kể chỉ được trả một lần.
  • Đặt nhập khẩu trong một chức năng sẽ khiến các cuộc gọi đến chức năng đó mất nhiều thời gian hơn.

Vì vậy, nếu bạn quan tâm đến hiệu quả, hãy đặt hàng nhập khẩu lên hàng đầu. Chỉ di chuyển chúng vào một chức năng nếu hồ sơ của bạn cho thấy điều đó sẽ giúp ích (bạn đã lập hồ sơ để xem nơi nào tốt nhất để cải thiện hiệu suất, phải không ??)


Những lý do tốt nhất tôi từng thấy để thực hiện nhập khẩu lười biếng là:

  • Hỗ trợ thư viện tùy chọn. Nếu mã của bạn có nhiều đường dẫn sử dụng các thư viện khác nhau, đừng ngắt nếu thư viện tùy chọn không được cài đặt.
  • Trong __init__.pymột plugin, có thể được nhập nhưng không thực sự được sử dụng. Ví dụ là các plugin Bazaar, sử dụng bzrlibkhung tải lười biếng.

17
John, đây là một câu hỏi hoàn toàn lý thuyết vì vậy tôi không có bất kỳ mã nào để lập hồ sơ. Trước đây tôi luôn theo PEP, nhưng tôi đã viết một số mã ngày hôm nay khiến tôi tự hỏi liệu đó có phải là điều đúng đắn để làm. Cảm ơn bạn đã giúp đỡ.
Adam J. Forster

43
> Đặt việc nhập trong một chức năng sẽ khiến các cuộc gọi đến chức năng đó mất nhiều thời gian hơn. Trên thực tế, tôi nghĩ rằng chi phí này chỉ được trả cho một lần. Tôi đã đọc rằng Python lưu trữ một mô-đun đã nhập để chỉ có chi phí tối thiểu để nhập lại mô-đun.
moltenform

24
@halfhourhacks Python sẽ không nhập lại mô-đun, nhưng nó vẫn phải thực hiện một vài hướng dẫn chỉ để xem mô-đun có tồn tại / có trong sys.modules / v.v.
John Millikin

24
-1. Đặt nhập khẩu trong một chức năng không nhất thiết phải làm cho nó mất nhiều thời gian hơn. Xin vui lòng xem câu trả lời của tôi trên một câu hỏi khác.
aaronasterling

4
Một trường hợp sử dụng là tránh nhập khẩu tròn (thường không hợp lý, nhưng đôi khi nó phù hợp). Đôi khi, lớp A trong mô-đun m1 gọi một phương thức trên lớp B trong mô-đun m2, nó xây dựng một thể hiện khác của lớp A. Nếu phương thức trong lớp B xây dựng một thể hiện của lớp A chỉ có hoạt động nhập khi thực hiện chức năng xây dựng một thể hiện, nhập khẩu tròn được tránh.
Sam Svenbjorgchristiensensen

80

Đặt câu lệnh nhập bên trong hàm có thể ngăn chặn các phụ thuộc vòng tròn. Ví dụ: nếu bạn có 2 mô-đun, X.py và Y.py và cả hai đều cần nhập lẫn nhau, điều này sẽ gây ra sự phụ thuộc vòng tròn khi bạn nhập một trong các mô-đun gây ra một vòng lặp vô hạn. Nếu bạn di chuyển câu lệnh nhập vào một trong các mô-đun thì nó sẽ không cố nhập mô-đun khác cho đến khi hàm được gọi và mô-đun đó sẽ được nhập, do đó không có vòng lặp vô hạn. Đọc ở đây để biết thêm - effbot.org/zone/import-confusion.htmlm


3
Có nhưng người ta có thể rơi vào địa ngục phụ thuộc.
eigenein

8
Nếu hai mô-đun cần nhập lẫn nhau, có gì đó sai nghiêm trọng với mã.
Anna

Lập trình hướng đối tượng thường dẫn tôi đến sự phụ thuộc vòng tròn. Một lớp đối tượng quan trọng có thể được nhập vào một số mô-đun. Để đối tượng này thực hiện các nhiệm vụ riêng của mình, nó có thể cần phải tiếp cận với một hoặc nhiều mô-đun đó. Có nhiều cách để tránh nó, chẳng hạn như gửi dữ liệu đến đối tượng thông qua hàm args, để cho phép nó truy cập vào mô-đun khác. Nhưng có những lúc khi làm điều này cảm thấy rất phản trực giác đối với OOP (thế giới bên ngoài không cần phải biết cách hoàn thành nhiệm vụ trong chức năng đó).
Robert

4
Khi X cần Y và Y cần X, chúng là hai phần của cùng một ý tưởng (nghĩa là phải được xác định cùng nhau) hoặc thiếu một sự trừu tượng hóa.
GLRoman

59

Tôi đã áp dụng thực tiễn đặt tất cả các nhập khẩu trong các chức năng sử dụng chúng, thay vì ở đầu mô-đun.

Lợi ích tôi nhận được là khả năng tái cấu trúc đáng tin cậy hơn. Khi tôi di chuyển một chức năng từ mô-đun này sang mô-đun khác, tôi biết rằng chức năng này sẽ tiếp tục hoạt động với tất cả các di sản thử nghiệm còn nguyên vẹn. Nếu tôi có nhập khẩu của tôi ở đầu mô-đun, khi tôi di chuyển một chức năng, tôi thấy rằng cuối cùng tôi đã dành rất nhiều thời gian để nhập khẩu mô-đun mới hoàn thành và tối thiểu. Một IDE tái cấu trúc có thể làm cho điều này không liên quan.

Có một hình phạt tốc độ như đã đề cập ở nơi khác. Tôi đã đo lường điều này trong ứng dụng của mình và thấy nó không đáng kể cho mục đích của tôi.

Thật tuyệt khi có thể thấy tất cả các phụ thuộc mô-đun lên phía trước mà không cần dùng đến tìm kiếm (ví dụ: grep). Tuy nhiên, lý do tôi quan tâm đến các phụ thuộc mô-đun nói chung là vì tôi đang cài đặt, tái cấu trúc hoặc di chuyển toàn bộ hệ thống bao gồm nhiều tệp chứ không chỉ là một mô-đun. Trong trường hợp đó, dù sao tôi cũng sẽ thực hiện tìm kiếm toàn cầu để đảm bảo tôi có các phụ thuộc ở cấp hệ thống. Vì vậy, tôi đã không tìm thấy hàng nhập khẩu toàn cầu để hỗ trợ sự hiểu biết của tôi về một hệ thống trong thực tế.

Tôi thường đặt việc nhập sysbên trong if __name__=='__main__'kiểm tra và sau đó chuyển các đối số (như sys.argv[1:]) cho một main()hàm. Điều này cho phép tôi sử dụng maintrong bối cảnh syschưa được nhập.


4
Nhiều IDE dễ dàng tái cấu trúc mã này bằng cách tối ưu hóa và tự động nhập các mô-đun cần thiết vào tệp của bạn. Trong phần lớn các trường hợp, PyCharm và Eclipse đã đưa ra quyết định chính xác cho tôi. Tôi cá là có một cách để có được hành vi tương tự trong emacs hoặc vim.
brent.payne

3
Một nhập khẩu bên trong một câu lệnh if trong không gian tên toàn cầu vẫn là một nhập khẩu toàn cầu. Điều này sẽ in các đối số (sử dụng Python 3): def main(): print(sys.argv); if True: import sys; main();Bạn sẽ phải bọc if __name__=='__main__'trong một hàm để tạo một không gian tên mới.
Darcinon

4
Điều này gây ấn tượng với tôi như là một lý do tuyệt vời để nhập khẩu trong các chức năng hơn là trong phạm vi toàn cầu. Tôi khá ngạc nhiên khi không có ai khác đề cập đến việc đó vì lý do tương tự. Có bất kỳ nhược điểm đáng kể nào, bên cạnh hiệu suất và tính dài dòng?
tảo

@ achal nhược điểm là nhiều người trăn ghét điều này vì bạn vi phạm luật cod. Bạn phải thuyết phục các thành viên trong nhóm của bạn. Hình phạt hiệu suất là tối thiểu. Đôi khi nó còn nhanh hơn nữa, hãy xem stackoverflow.com/a/4789963/362951
mit

Tôi thấy nó cực kỳ hữu ích cho việc tái cấu trúc để đặt hàng nhập khẩu gần với nơi tôi sử dụng chúng. Không còn cần thiết phải cuộn lên đầu và quay lại quá nhiều thời gian. Tôi sử dụng các IDE như pycharm hoặc wing ide và cũng sử dụng tái cấu trúc của chúng, nhưng tôi không luôn muốn dựa vào chúng. Việc chuyển các chức năng sang một mô-đun khác trở nên dễ dàng hơn rất nhiều với kiểu nhập thay thế này, do đó, tôi tái cấu trúc nhiều hơn nữa.
mit

39

Hầu hết thời gian điều này sẽ hữu ích cho sự rõ ràng và hợp lý để làm nhưng không phải lúc nào cũng như vậy. Dưới đây là một vài ví dụ về trường hợp nhập khẩu mô-đun có thể sống ở nơi khác.

Đầu tiên, bạn có thể có một mô-đun với bài kiểm tra đơn vị có dạng:

if __name__ == '__main__':
    import foo
    aa = foo.xyz()         # initiate something for the test

Thứ hai, bạn có thể có yêu cầu nhập có điều kiện một số mô-đun khác nhau trong thời gian chạy.

if [condition]:
    import foo as plugin_api
else:
    import bar as plugin_api
xx = plugin_api.Plugin()
[...]

Có thể có các tình huống khác mà bạn có thể đặt nhập khẩu trong các phần khác trong mã.


14

Biến thể đầu tiên thực sự hiệu quả hơn biến thể thứ hai khi hàm được gọi là 0 hoặc 1 lần. Tuy nhiên, với các yêu cầu thứ hai và tiếp theo, cách tiếp cận "nhập mọi cuộc gọi" thực sự kém hiệu quả hơn. Xem liên kết này để biết kỹ thuật tải lười biếng kết hợp tốt nhất cả hai phương pháp bằng cách thực hiện "nhập khẩu lười biếng".

Nhưng có những lý do khác ngoài hiệu quả tại sao bạn có thể thích cái này hơn cái kia. Một cách tiếp cận là làm cho nó rõ ràng hơn nhiều đối với người đọc mã về các phụ thuộc mà mô-đun này có. Chúng cũng có các đặc điểm thất bại rất khác nhau - lần đầu tiên sẽ thất bại khi tải nếu không có mô-đun "datetime" trong khi lần thứ hai sẽ không thất bại cho đến khi phương thức được gọi.

Lưu ý thêm: Trong IronPython, nhập khẩu có thể đắt hơn một chút so với CPython vì về cơ bản mã được biên dịch khi được nhập.


1
Không đúng khi người đầu tiên hoạt động tốt hơn: wiki.python.org/moin/PythonSpeed/ Khăn
Jason Baker

Nó thực hiện tốt hơn nếu phương thức không bao giờ được gọi vì quá trình nhập không bao giờ xảy ra.
Curt Hagenlocher

Đúng, nhưng nó hoạt động kém hơn nếu phương thức được gọi nhiều hơn một lần. Và lợi ích hiệu suất bạn sẽ có được từ việc không nhập mô-đun ngay lập tức là không đáng kể trong hầu hết các trường hợp. Các ngoại lệ sẽ là nếu mô-đun rất lớn hoặc có rất nhiều loại chức năng này.
Jason Baker

Trong thế giới IronPython, nhập khẩu ban đầu đắt hơn nhiều so với CPython;). Ví dụ "nhập khẩu lười biếng" trong liên kết của bạn có lẽ là giải pháp chung chung tốt nhất.
Curt Hagenlocher

Tôi hy vọng bạn không phiền, nhưng tôi đã chỉnh sửa nó vào bài viết của bạn. Đó là thông tin hữu ích để biết.
Jason Baker

9

Curt đưa ra một điểm tốt: phiên bản thứ hai rõ ràng hơn và sẽ thất bại ở thời gian tải thay vì muộn hơn và bất ngờ.

Thông thường tôi không lo lắng về hiệu quả của việc tải các mô-đun, vì nó (a) khá nhanh và (b) chủ yếu chỉ xảy ra khi khởi động.

Nếu bạn phải tải các mô-đun nặng vào những thời điểm không mong muốn, có thể có ý nghĩa hơn khi tải chúng một cách linh hoạt với __import__chức năng và chắc chắn nắm bắt các ImportErrorngoại lệ và xử lý chúng một cách hợp lý.


8

Tôi sẽ không lo lắng về hiệu quả của việc tải mô-đun lên phía trước quá nhiều. Bộ nhớ được sử dụng bởi mô-đun sẽ không quá lớn (giả sử nó đủ mô-đun) và chi phí khởi động sẽ không đáng kể.

Trong hầu hết các trường hợp, bạn muốn tải các mô-đun ở đầu tệp nguồn. Đối với ai đó đọc mã của bạn, việc cho biết chức năng hoặc đối tượng đến từ mô-đun nào sẽ dễ dàng hơn nhiều.

Một lý do chính đáng để nhập một mô-đun ở nơi khác trong mã là nếu nó được sử dụng trong câu lệnh gỡ lỗi.

Ví dụ:

do_something_with_x(x)

Tôi có thể gỡ lỗi này với:

from pprint import pprint
pprint(x)
do_something_with_x(x)

Tất nhiên, lý do khác để nhập các mô-đun ở nơi khác trong mã là nếu bạn cần nhập động chúng. Điều này là do bạn không có nhiều sự lựa chọn.

Tôi sẽ không lo lắng về hiệu quả của việc tải mô-đun lên phía trước quá nhiều. Bộ nhớ được sử dụng bởi mô-đun sẽ không quá lớn (giả sử nó đủ mô-đun) và chi phí khởi động sẽ không đáng kể.


Chúng ta đang nói về hàng chục mili giây chi phí khởi động cho mỗi mô-đun (trên máy của tôi). Điều đó không phải lúc nào cũng không đáng kể, ví dụ nếu nó ảnh hưởng đến khả năng phản hồi của ứng dụng web đối với người dùng nhấp chuột.
Evgeni Sergeev

6

Đó là một sự đánh đổi, mà chỉ lập trình viên mới có thể quyết định thực hiện.

Trường hợp 1 tiết kiệm một số bộ nhớ và thời gian khởi động bằng cách không nhập mô-đun datetime (và thực hiện bất kỳ khởi tạo nào mà nó có thể yêu cầu) cho đến khi cần. Lưu ý rằng thực hiện nhập 'chỉ khi được gọi' cũng có nghĩa là thực hiện 'mỗi lần được gọi', vì vậy mỗi cuộc gọi sau lần đầu tiên vẫn phát sinh thêm chi phí thực hiện nhập.

Trường hợp 2 tiết kiệm một số thời gian thực hiện và độ trễ bằng cách nhập datetime trước để not_often_called () sẽ trả lại nhanh hơn khi được gọi và cũng không phải chịu chi phí nhập khẩu cho mỗi cuộc gọi.

Bên cạnh hiệu quả, sẽ dễ dàng thấy các phụ thuộc mô-đun lên phía trước nếu các báo cáo nhập khẩu ... ở phía trước. Việc giấu chúng trong mã có thể khiến việc tìm kiếm mô-đun gì đó phụ thuộc vào khó khăn hơn.

Cá nhân tôi thường tuân theo PEP ngoại trừ những thứ như kiểm tra đơn vị và vì vậy tôi không muốn luôn luôn được tải vì tôi biết chúng sẽ không được sử dụng ngoại trừ mã kiểm tra.


2
-1. Chi phí chính của việc nhập khẩu chỉ xảy ra lần đầu tiên. Chi phí sys.modulestra cứu mô-đun trong có thể dễ dàng được bù đắp bằng khoản tiết kiệm khi chỉ phải tra cứu tên địa phương thay vì tên toàn cầu.
aaronasterling

6

Đây là một ví dụ trong đó tất cả các hàng nhập khẩu đều ở trên cùng (đây là lần duy nhất tôi cần để làm điều này). Tôi muốn có thể chấm dứt một quy trình con trên cả Un * x và Windows.

import os
# ...
try:
    kill = os.kill  # will raise AttributeError on Windows
    from signal import SIGTERM
    def terminate(process):
        kill(process.pid, SIGTERM)
except (AttributeError, ImportError):
    try:
        from win32api import TerminateProcess  # use win32api if available
        def terminate(process):
            TerminateProcess(int(process._handle), -1)
    except ImportError:
        def terminate(process):
            raise NotImplementedError  # define a dummy function

(Về đánh giá: những gì John Millikin đã nói.)


6

Điều này cũng giống như nhiều tối ưu hóa khác - bạn hy sinh một số khả năng đọc cho tốc độ. Như John đã đề cập, nếu bạn đã hoàn thành bài tập về nhà của mình và thấy đây là một thay đổi đủ hữu ích đáng kể bạn cần thêm tốc độ, thì hãy thực hiện nó. Có lẽ sẽ tốt khi ghi chú lên tất cả các hàng nhập khác:

from foo import bar
from baz import qux
# Note: datetime is imported in SomeClass below

4

Khởi tạo mô-đun chỉ xảy ra một lần - trong lần nhập đầu tiên. Nếu mô-đun được đề cập là từ thư viện tiêu chuẩn, thì có khả năng bạn cũng sẽ nhập nó từ các mô-đun khác trong chương trình của mình. Đối với một mô-đun phổ biến như datetime, nó cũng có khả năng phụ thuộc vào một loạt các thư viện tiêu chuẩn khác. Báo cáo nhập khẩu sẽ có giá rất ít sau đó vì việc thâm nhập mô-đun đã xảy ra rồi. Tất cả những gì nó đang làm tại thời điểm này là ràng buộc đối tượng mô-đun hiện có vào phạm vi cục bộ.

Kết hợp thông tin đó với đối số để dễ đọc và tôi sẽ nói rằng tốt nhất là có câu lệnh nhập ở phạm vi mô-đun.


4

Chỉ cần hoàn thành câu trả lời của Moe và câu hỏi ban đầu:

Khi chúng ta phải đối phó với sự phụ thuộc vòng tròn, chúng ta có thể thực hiện một số "thủ thuật". Giả sử chúng ta đang làm việc với các mô-đun a.pyb.pycó chứa x()và b y(), tương ứng. Sau đó:

  1. Chúng ta có thể di chuyển một trong các from importsở dưới cùng của mô-đun.
  2. Chúng ta có thể di chuyển một from importstrong các chức năng hoặc phương thức thực sự yêu cầu nhập (điều này không phải lúc nào cũng có thể, vì bạn có thể sử dụng nó từ một số nơi).
  3. Chúng tôi có thể thay đổi một trong hai from importsthành một bản nhập giống như:import a

Vì vậy, để kết luận. Nếu bạn không xử lý các phụ thuộc vòng tròn và thực hiện một số mẹo để tránh chúng, thì tốt hơn hết là đặt tất cả hàng nhập khẩu của bạn lên hàng đầu vì những lý do đã được giải thích trong các câu trả lời khác cho câu hỏi này. Và xin vui lòng, khi thực hiện "thủ thuật" này bao gồm một nhận xét, nó luôn được chào đón! :)


4

Ngoài các câu trả lời xuất sắc đã được đưa ra, đáng chú ý rằng vị trí nhập khẩu không chỉ là vấn đề về phong cách. Đôi khi, một mô-đun có các phụ thuộc ngầm định cần được nhập hoặc khởi tạo trước, và nhập khẩu cấp cao nhất có thể dẫn đến vi phạm trật tự thực hiện được yêu cầu.

Vấn đề này thường xuất hiện trong API Python của Apache Spark, nơi bạn cần khởi tạo SparkContext trước khi nhập bất kỳ gói hoặc mô-đun pyspark nào. Tốt nhất là đặt nhập khẩu pyspark trong phạm vi mà SparkContext được đảm bảo có sẵn.


4

Tôi đã ngạc nhiên khi không thấy số chi phí thực tế cho các kiểm tra tải lặp đi lặp lại đã được đăng, mặc dù có nhiều giải thích tốt về những gì mong đợi.

Nếu bạn nhập ở trên cùng, bạn thực hiện tải trọng bất kể điều gì. Điều đó khá nhỏ, nhưng thường là trong một phần nghìn giây, không phải là nano giây.

Nếu bạn nhập trong (các) chức năng, thì bạn chỉ thực hiện lần truy cập để tải nếukhi một trong các chức năng đó được gọi lần đầu tiên. Như nhiều người đã chỉ ra, nếu điều đó hoàn toàn không xảy ra, bạn sẽ tiết kiệm được thời gian tải. Nhưng nếu (các) hàm được gọi rất nhiều, bạn thực hiện một lần nhấn mặc dù nhỏ hơn nhiều (để kiểm tra xem nó đã được tải chưa, không thực sự tải lại). Mặt khác, như @aaronasterling chỉ ra rằng bạn cũng tiết kiệm được một chút vì nhập trong hàm cho phép hàm sử dụng tra cứu biến cục bộ nhanh hơn một chút để xác định tên sau ( http://stackoverflow.com/questions/477096/python- nhập mã hóa-phong cách / 4789963 # 4789963 ).

Dưới đây là kết quả của một thử nghiệm đơn giản, nhập một vài thứ từ bên trong hàm. Thời gian được báo cáo (trong Python 2.7,14 trên Intel Core i7 2,3 GHz) được hiển thị bên dưới (cuộc gọi thứ 2 thực hiện nhiều hơn các cuộc gọi sau có vẻ phù hợp, mặc dù tôi không biết tại sao).

 0 foo:   14429.0924 µs
 1 foo:      63.8962 µs
 2 foo:      10.0136 µs
 3 foo:       7.1526 µs
 4 foo:       7.8678 µs
 0 bar:       9.0599 µs
 1 bar:       6.9141 µs
 2 bar:       7.1526 µs
 3 bar:       7.8678 µs
 4 bar:       7.1526 µs

Mật mã:

from __future__ import print_function
from time import time

def foo():
    import collections
    import re
    import string
    import math
    import subprocess
    return

def bar():
    import collections
    import re
    import string
    import math
    import subprocess
    return

t0 = time()
for i in xrange(5):
    foo()
    t1 = time()
    print("    %2d foo: %12.4f \xC2\xB5s" % (i, (t1-t0)*1E6))
    t0 = t1
for i in xrange(5):
    bar()
    t1 = time()
    print("    %2d bar: %12.4f \xC2\xB5s" % (i, (t1-t0)*1E6))
    t0 = t1

Những thay đổi trong thời gian chạy có thể là do quy mô tần số CPU đáp ứng với tải. Tốt hơn là bắt đầu kiểm tra tốc độ với một giây công việc bận rộn để tăng tốc độ xung nhịp CPU.
Han-Kwang Nienhuys

3

Tôi không khao khát cung cấp câu trả lời đầy đủ, bởi vì những người khác đã làm điều này rất tốt. Tôi chỉ muốn đề cập đến một trường hợp sử dụng khi tôi thấy đặc biệt hữu ích để nhập các mô-đun bên trong các chức năng. Ứng dụng của tôi sử dụng các gói và mô-đun python được lưu trữ ở một số vị trí nhất định làm plugin. Trong quá trình khởi động ứng dụng, ứng dụng sẽ đi qua tất cả các mô-đun trong vị trí và nhập chúng, sau đó nó nhìn vào bên trong các mô-đun và nếu nó tìm thấy một số điểm gắn kết cho các plugin (trong trường hợp của tôi, nó là một lớp con của một lớp cơ sở nhất định có duy nhất ID) nó đăng ký chúng. Số lượng plugin rất lớn (hiện tại là hàng chục, nhưng có thể hàng trăm trong tương lai) và mỗi plugin được sử dụng khá hiếm. Việc nhập các thư viện của bên thứ ba ở đầu các mô-đun plugin của tôi là một chút hình phạt trong quá trình khởi động ứng dụng. Đặc biệt là một số thư viện của bên thứ ba rất khó nhập (ví dụ: nhập vào âm mưu thậm chí cố gắng kết nối với internet và tải xuống một cái gì đó thêm khoảng một giây để khởi động). Bằng cách tối ưu hóa nhập khẩu (chỉ gọi chúng trong các chức năng sử dụng chúng) trong các plugin tôi đã quản lý để thu nhỏ khởi động từ 10 giây xuống còn 2 giây. Đó là một sự khác biệt lớn cho người dùng của tôi.

Vì vậy, câu trả lời của tôi là không, không phải lúc nào cũng đặt hàng nhập khẩu lên hàng đầu trong các mô-đun của bạn.


3

Điều thú vị là cho đến nay, không có một câu trả lời nào được đề cập đến việc xử lý song song, trong đó có thể BẮT BUỘC rằng các mục nhập trong hàm, khi mã chức năng được tuần tự hóa là những gì đang được đẩy xung quanh các lõi khác, ví dụ như trong trường hợp của ipyabul.


1

Có thể đạt được hiệu suất bằng cách nhập các biến / phạm vi cục bộ bên trong hàm. Điều này phụ thuộc vào việc sử dụng điều được nhập bên trong hàm. Nếu bạn đang lặp nhiều lần và truy cập một đối tượng toàn cầu mô-đun, nhập nó dưới dạng cục bộ có thể giúp ích.

kiểm tra

X=10
Y=11
Z=12
def add(i):
  i = i + 10

runlocal.py

from test import add, X, Y, Z

    def callme():
      x=X
      y=Y
      z=Z
      ladd=add 
      for i  in range(100000000):
        ladd(i)
        x+y+z

    callme()

run.py

from test import add, X, Y, Z

def callme():
  for i in range(100000000):
    add(i)
    X+Y+Z

callme()

Một thời gian trên Linux cho thấy một lợi ích nhỏ

/usr/bin/time -f "\t%E real,\t%U user,\t%S sys" python run.py 
    0:17.80 real,   17.77 user, 0.01 sys
/tmp/test$ /usr/bin/time -f "\t%E real,\t%U user,\t%S sys" python runlocal.py 
    0:14.23 real,   14.22 user, 0.01 sys

thực là đồng hồ treo tường. người dùng là thời gian trong chương trình. Sys là thời gian cho các cuộc gọi hệ thống.

https://docs.python.org/3.5/reference/executmodel.html#resolution-of-names


1

Dễ đọc

Ngoài hiệu suất khởi động, có một đối số dễ đọc được đưa ra để bản địa hóa các importcâu lệnh. Ví dụ: lấy số dòng python 1283 đến 1296 trong dự án python đầu tiên hiện tại của tôi:

listdata.append(['tk font version', font_version])
listdata.append(['Gtk version', str(Gtk.get_major_version())+"."+
                 str(Gtk.get_minor_version())+"."+
                 str(Gtk.get_micro_version())])

import xml.etree.ElementTree as ET

xmltree = ET.parse('/usr/share/gnome/gnome-version.xml')
xmlroot = xmltree.getroot()
result = []
for child in xmlroot:
    result.append(child.text)
listdata.append(['Gnome version', result[0]+"."+result[1]+"."+
                 result[2]+" "+result[3]])

Nếu importcâu lệnh nằm ở đầu tập tin, tôi sẽ phải cuộn lên một quãng đường dài hoặc nhấn Home, để tìm hiểu xem đó ETlà gì . Sau đó, tôi sẽ phải điều hướng trở lại dòng 1283 để tiếp tục đọc mã.

Thật vậy, ngay cả khi importcâu lệnh nằm ở đầu hàm (hoặc lớp) như nhiều người sẽ đặt nó, việc phân trang lên và xuống sẽ được yêu cầu.

Hiển thị số phiên bản Gnome sẽ hiếm khi được thực hiện, do đó, importở đầu tệp giới thiệu độ trễ khởi động không cần thiết.


0

Tôi muốn đề cập đến một usecase của tôi, rất giống với những người được đề cập bởi @John Millikin và @VK:

Nhập khẩu tùy chọn

Tôi thực hiện phân tích dữ liệu với Jupyter Notebook và tôi sử dụng cùng một sổ ghi chép IPython làm mẫu cho tất cả các phân tích. Trong một số trường hợp, tôi cần nhập Tensorflow để thực hiện một số mô hình chạy nhanh, nhưng đôi khi tôi làm việc ở những nơi mà tenorflow không được thiết lập / chậm nhập. Trong những trường hợp đó, tôi gói gọn các hoạt động phụ thuộc vào Tensorflow của mình trong một hàm trợ giúp, nhập dòng chảy căng bên trong hàm đó và liên kết nó với một nút.

Bằng cách này, tôi có thể thực hiện "khởi động lại và chạy tất cả" mà không phải chờ nhập, hoặc phải tiếp tục các ô còn lại khi thất bại.


0

Đây là một cuộc thảo luận hấp dẫn. Giống như nhiều người khác, tôi thậm chí chưa bao giờ xem xét chủ đề này. Tôi bị dồn vào chân tường khi phải nhập các chức năng vì muốn sử dụng Django ORM trong một trong các thư viện của mình. Tôi đã phải gọi django.setup()trước khi nhập các lớp mô hình của mình và vì đây là phần đầu của tệp nên nó đã bị kéo vào mã thư viện hoàn toàn không phải Django do cấu trúc trình tiêm IoC.

Tôi đã bị hack khoảng một chút và cuối cùng đã đưa django.setup()vào hàm tạo đơn lẻ và nhập khẩu có liên quan ở đầu mỗi phương thức lớp. Bây giờ điều này đã hoạt động tốt nhưng làm tôi khó chịu vì hàng nhập khẩu không đứng đầu và tôi cũng bắt đầu lo lắng về thời gian tăng thêm của hàng nhập khẩu. Sau đó, tôi đến đây và đọc với sự quan tâm lớn của mọi người về điều này.

Tôi có một nền tảng C ++ dài và hiện đang sử dụng Python / Cython. Tôi đồng ý với điều này là tại sao không đưa các mục nhập vào hàm trừ khi nó gây ra cho bạn một nút cổ chai. Nó chỉ giống như khai báo không gian cho các biến ngay trước khi bạn cần chúng. Vấn đề là tôi có hàng ngàn dòng mã với tất cả hàng nhập ở đầu! Vì vậy, tôi nghĩ rằng tôi sẽ làm điều đó từ bây giờ và thay đổi tập tin kỳ lạ ở đây và ở đó khi tôi đi qua và có thời gian.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.