Вопрос-Ответ

Should import statements always be at the top of a module?

Должны ли операторы импорта всегда находиться в верхней части модуля?

В PEP 8 говорится:


Импорт всегда помещается в начало файла, сразу после любых комментариев модуля и строк документации, а также перед глобальными значениями модуля и константами.


Однако, если класс / метод / функция, которые я импортирую, используются только в редких случаях, наверняка эффективнее выполнять импорт, когда это необходимо?

Не так ли:

class SomeClass(object):

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

эффективнее этого?

from datetime import datetime

class SomeClass(object):

def not_often_called(self)
self.datetime = datetime.now()
Переведено автоматически
Ответ 1

Импорт модуля происходит довольно быстро, но не мгновенно. Это означает, что:


  • Размещение импорта в верхней части модуля - это нормально, потому что это тривиальная стоимость, которая оплачивается только один раз.

  • Размещение импорта внутри функции приведет к тому, что вызовы этой функции будут занимать больше времени.

Итак, если вы заботитесь об эффективности, разместите импорт вверху. Перемещайте их в функцию только в том случае, если ваше профилирование показывает, что это поможет (вы сделали profile, чтобы увидеть, где лучше всего повысить производительность, верно ??)


Лучшие причины, которые я видел для выполнения отложенного импорта, следующие:


  • Поддержка дополнительных библиотек. Если в вашем коде есть несколько путей, использующих разные библиотеки, не прерывайте работу, если дополнительная библиотека не установлена.

  • В __init__.py плагине, который может быть импортирован, но фактически не используется. Примерами являются плагины Bazaar, которые используют bzrlib фреймворк с отложенной загрузкой.

Ответ 2

Помещение оператора импорта внутрь функции может предотвратить циклические зависимости. Например, если у вас есть 2 модуля, X.py и Y.py, и им обоим необходимо импортировать друг друга, это вызовет циклическую зависимость при импорте одного из модулей, вызывающую бесконечный цикл. Если вы переместите оператор импорта в один из модулей, то он не будет пытаться импортировать другой модуль, пока не будет вызвана функция, и этот модуль уже будет импортирован, поэтому бесконечного цикла не будет. Подробнее читайте здесь - effbot.org/zone/import-confusion.htm

Ответ 3

Я перенял практику размещения всего импорта в функциях, которые их используют, а не в верхней части модуля.

Преимущество, которое я получаю, - это возможность более надежного рефакторинга. Когда я перемещаю функцию из одного модуля в другой, я знаю, что функция продолжит работать со всем своим наследием тестирования без изменений. Если у меня есть мой импорт в верхней части модуля, то при перемещении функции я обнаруживаю, что в конечном итоге трачу много времени на то, чтобы импорт нового модуля был полным и минимальным. IDE для рефакторинга может сделать это неактуальным.

Существует ограничение скорости, как упоминалось в другом месте. Я измерил это в своем приложении и обнаружил, что оно незначительно для моих целей.

Также приятно иметь возможность видеть все зависимости модуля заранее, не прибегая к поиску (например, grep). Однако причина, по которой меня волнуют зависимости модулей, обычно заключается в том, что я устанавливаю, рефакторингую или перемещаю всю систему, состоящую из нескольких файлов, а не только один модуль. В таком случае я все равно собираюсь выполнить глобальный поиск, чтобы убедиться, что у меня есть зависимости системного уровня. Итак, я не нашел глобального импорта, который помог бы мне понять систему на практике.

Обычно я помещаю импорт sys внутри if __name__=='__main__' проверки, а затем передаю аргументы (например, sys.argv[1:]) в main() функцию. Это позволяет мне использовать main в контексте, где sys не был импортирован.

Ответ 4

В большинстве случаев это было бы полезно для наглядности и разумности выполнения, но это не всегда так. Ниже приведена пара примеров обстоятельств, при которых импорт модуля может выполняться в другом месте.

Во-первых, у вас мог бы быть модуль с модульным тестированием вида:

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

Во-вторых, у вас может возникнуть требование условно импортировать какой-то другой модуль во время выполнения.

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

Вероятно, существуют другие ситуации, когда вы могли бы размещать импорт в других частях кода.

2024-01-01 04:23 python