ОПТОСОЗ 08 гласит:
Импорт всегда помещается вверху файла, сразу после любых комментариев и строк документации, а также перед глобальными переменными и константами модуля.
Однако, если класс / метод / функция, которую я импортирую, используется только в редких случаях, несомненно, более эффективно выполнять импорт, когда это необходимо?
Разве это не:
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()
источник
Помещение оператора import внутри функции может предотвратить циклические зависимости. Например, если у вас есть 2 модуля, X.py и Y.py, и им обоим необходимо импортировать друг друга, это приведет к циклической зависимости при импорте одного из модулей, вызывая бесконечный цикл. Если вы переместите оператор импорта в один из модулей, он не будет пытаться импортировать другой модуль до тех пор, пока не будет вызвана функция, и этот модуль уже будет импортирован, поэтому бесконечный цикл отсутствует. Подробнее читайте здесь - effbot.org/zone/import-confusion.htm
источник
Я принял практику помещения всего импорта в функции, которые их используют, а не в верхнюю часть модуля.
Преимущество, которое я получаю, - это возможность более надежного рефакторинга. Когда я перемещаю функцию из одного модуля в другой, я знаю, что эта функция будет продолжать работать со всем ее наследием неповрежденного тестирования. Если мой импорт находится вверху модуля, когда я перемещаю функцию, я обнаруживаю, что трачу много времени на то, чтобы импорт нового модуля был полным и минимальным. Рефакторинг IDE может сделать это неактуальным.
Как упоминалось в другом месте, есть штраф за скорость. Я измерил это в своем заявлении и нашел, что это несущественно для моих целей.
Также приятно иметь возможность видеть все зависимости модуля заранее, не прибегая к поиску (например, grep). Однако причина, по которой я беспокоюсь о зависимостях модулей, обычно заключается в том, что я устанавливаю, реорганизую или перемещаю всю систему, содержащую несколько файлов, а не только один модуль. В этом случае я все равно собираюсь выполнить глобальный поиск, чтобы убедиться, что у меня есть зависимости на уровне системы. Поэтому я не нашел глобального импорта, который помог бы мне понять систему на практике.
Я обычно помещаю импорт
sys
внутриif __name__=='__main__'
проверки, а затем передаю аргументы (напримерsys.argv[1:]
)main()
функции. Это позволяет мне использоватьmain
в контексте, гдеsys
не было импортировано.источник
def main(): print(sys.argv); if True: import sys; main();
вам нужно будет обернутьif __name__=='__main__'
функцию, чтобы создать новое пространство имен.В большинстве случаев это было бы полезно для ясности и целесообразности, но это не всегда так. Ниже приведено несколько примеров обстоятельств, когда импорт модулей может происходить в другом месте.
Во-первых, у вас может быть модуль с модульным тестом в форме:
Во-вторых, у вас может возникнуть необходимость условно импортировать какой-то другой модуль во время выполнения.
Возможно, есть другие ситуации, когда вы можете поместить импорт в другие части кода.
источник
Первый вариант действительно более эффективен, чем второй, когда функция вызывается либо ноль, либо один раз. Однако при втором и последующих вызовах подход «импортировать каждый вызов» на самом деле менее эффективен. Посмотрите эту ссылку для техники отложенной загрузки, которая сочетает в себе лучшее из обоих подходов, выполняя «отложенный импорт».
Но есть и другие причины, помимо эффективности, почему вы можете предпочесть одну над другой. Один из подходов делает его более понятным для читателя кода относительно зависимостей этого модуля. Они также имеют очень разные характеристики сбоев - первый потерпит неудачу во время загрузки, если не будет модуля «datetime», а второй не потерпит неудачу, пока метод не будет вызван.
Добавлено примечание: в IronPython импорт может быть немного дороже, чем в CPython, потому что код в основном компилируется при импорте.
источник
Курт делает хорошее замечание: вторая версия более понятна и потерпит неудачу во время загрузки, а не позже и неожиданно.
Обычно я не беспокоюсь об эффективности загрузки модулей, поскольку она (а) довольно быстрая и (б) в основном происходит только при запуске.
Если вам приходится загружать тяжеловесные модули в неожиданные моменты времени, возможно, имеет смысл загружать их динамически с помощью
__import__
функции и обязательно перехватыватьImportError
исключения и обрабатывать их разумным образом.источник
Я бы не стал слишком сильно беспокоиться об эффективности загрузки модуля. Объем памяти, занимаемой модулем, не будет очень большим (при условии, что он достаточно модульный), а стоимость запуска будет незначительной.
В большинстве случаев вы хотите загрузить модули в верхней части исходного файла. Для того, кто читает ваш код, гораздо проще определить, какая функция или объект пришли из какого модуля.
Одна хорошая причина импортировать модуль в другое место кода, если он используется в операторе отладки.
Например:
Я мог бы отладить это с:
Конечно, другая причина для импорта модулей в другое место кода - это необходимость их динамического импорта. Это потому, что у тебя практически нет выбора.
Я бы не стал слишком сильно беспокоиться об эффективности загрузки модуля. Объем памяти, занимаемой модулем, не будет очень большим (при условии, что он достаточно модульный), а стоимость запуска будет незначительной.
источник
Это компромисс, который может решить только программист.
Вариант 1 экономит некоторую память и время запуска, не импортируя модуль datetime (и выполняя любую инициализацию, которая может потребоваться) до тех пор, пока он не понадобится. Обратите внимание, что выполнение импорта «только при вызове» также означает выполнение его «каждый раз при вызове», поэтому каждый вызов после первого по-прежнему сопряжен с дополнительными издержками при выполнении импорта.
Случай 2 сэкономить время исполнения и латентность путем импорта DateTime заранее , так что not_often_called () будет возвращать более быстро , когда это является называется, а также не подвергаясь накладными расходами импорта при каждом вызове.
Помимо эффективности, проще увидеть зависимости модуля заранее, если операторы импорта ... заранее. Скрытие их в коде может затруднить поиск модулей, от которых что-то зависит.
Лично я обычно следую PEP за исключением таких вещей, как модульные тесты и такие, которые я не хочу всегда загружать, потому что я знаю, что они не будут использоваться, за исключением тестового кода.
источник
sys.modules
может быть легко компенсирована экономией, связанной только с поиском локального имени вместо глобального имени.Вот пример, где весь импорт находится на самом верху (это единственный раз, когда мне нужно было это сделать). Я хочу иметь возможность завершить подпроцесс в Un * x и Windows.
(На рассмотрении: что сказал Джон Милликин .)
источник
Это как и многие другие оптимизации - вы жертвуете некоторой читабельностью ради скорости. Как упоминал Джон, если вы выполнили домашнюю работу по профилированию и обнаружили, что это достаточно полезное изменение, и вам нужна дополнительная скорость, сделайте это. Вероятно, было бы неплохо добавить примечание ко всем другим импортам:
источник
Инициализация модуля происходит только один раз - при первом импорте. Если данный модуль взят из стандартной библиотеки, то вы, скорее всего, импортируете его и из других модулей вашей программы. Для модуля, столь же распространенного, как datetime, он также может зависеть от множества других стандартных библиотек. Тогда оператор import будет стоить очень мало, поскольку инициализация модуля уже произошла. Все, что он делает на этом этапе, привязывает существующий объект модуля к локальной области видимости.
Соедините эту информацию с аргументом для читабельности, и я бы сказал, что лучше всего иметь оператор import в области видимости модуля.
источник
Просто чтобы завершить ответ Мо и оригинальный вопрос:
Когда нам приходится иметь дело с циклическими зависимостями, мы можем делать некоторые «трюки». Предположим , что мы работаем с модулями
a.py
иb.py
которые содержатx()
и бy()
соответственно. Затем:from imports
в нижней части модуля.from imports
функций или методов, которые фактически требуют импорта (это не всегда возможно, так как вы можете использовать его из нескольких мест).from imports
на импорт, который выглядит следующим образом:import a
Итак, в заключение. Если вы не имеете дело с циклическими зависимостями и не выполняете какие-то уловки, чтобы их избежать, то лучше поставить весь ваш импорт на первое место по причинам, уже объясненным в других ответах на этот вопрос. И пожалуйста, при выполнении этого «трюка» включайте комментарий, это всегда приветствуется! :)
источник
В дополнение к уже полученным превосходным ответам стоит отметить, что размещение импорта - это не просто вопрос стиля. Иногда модуль имеет неявные зависимости, которые необходимо сначала импортировать или инициализировать, а импорт верхнего уровня может привести к нарушению требуемого порядка выполнения.
Эта проблема часто возникает в Python API Apache Spark, где вам нужно инициализировать SparkContext перед импортом любых пакетов или модулей pyspark. Лучше всего размещать импорт pyspark в области, где SparkContext гарантированно будет доступен.
источник
Я был удивлен, не увидев фактических номеров затрат для повторных проверок нагрузки, опубликованных уже, хотя есть много хороших объяснений того, что ожидать.
Если вы импортируете сверху, вы принимаете загрузку, несмотря ни на что. Это довольно мало, но обычно в миллисекундах, а не наносекундах.
Если вы импортируете в функцию (и), вы получите удар только для загрузки, если и когда одна из этих функций вызывается впервые. Как уже отмечали многие, если этого не произойдет, вы сэкономите время загрузки. Но если функция (и) вызывается много раз, вы получаете повторное, хотя и гораздо меньшее попадание (для проверки того, что оно было загружено; не для фактической повторной загрузки). С другой стороны, как отметил @aaronasterling, вы также немного экономите, потому что импорт внутри функции позволяет функции использовать немного более быстрый поиск локальных переменных для идентификации имени позже ( http://stackoverflow.com/questions/477096/python- import-coding-style / 4789963 # 4789963 ).
Вот результаты простого теста, который импортирует несколько вещей из функции. Время, о котором сообщается (в Python 2.7.14 на 2,3-ГГц Intel Core i7), показано ниже (2-ой вызов, принимающий больше, чем более поздние вызовы, кажется последовательным, хотя я не знаю почему).
Код:
источник
Я не стремлюсь дать полный ответ, потому что другие уже сделали это очень хорошо. Я просто хочу упомянуть один случай использования, когда я нахожу особенно полезным импортировать модули внутри функций. Мое приложение использует пакеты и модули Python, хранящиеся в определенном месте, в качестве плагинов. Во время запуска приложения приложение просматривает все модули в расположении и импортирует их, затем просматривает модули и, если оно находит точки подключения для плагинов (в моем случае это подкласс определенного базового класса, имеющий уникальный ID) он их регистрирует. Количество плагинов велико (сейчас их десятки, а может, и сотни), и каждый из них используется довольно редко. Импортирование сторонних библиотек поверх моих модулей плагинов было небольшим штрафом при запуске приложения. Особенно тяжело импортировать некоторые сторонние библиотеки (например, импорт данных из плотно даже пытается подключиться к Интернету и загрузить что-то, что добавляло около одной секунды к запуску). Оптимизировав импорт (вызывая их только в тех функциях, где они используются) в плагинах, мне удалось сократить запуск с 10 до примерно 2 секунд. Это большая разница для моих пользователей.
Так что мой ответ - нет, не всегда помещайте импорт в верхнюю часть ваших модулей.
источник
Интересно, что ни в одном ответе не упоминалась параллельная обработка, где может потребоваться, чтобы импорт выполнялся в функции, когда сериализованный код функции - это то, что передается другим ядрам, например, как в случае ipyparallel.
источник
Может быть выигрыш в производительности, импортируя переменные / локальную область видимости внутри функции. Это зависит от использования импортируемой вещи внутри функции. Если вы многократно повторяете цикл и обращаетесь к глобальному объекту модуля, может помочь его импорт как локальный.
test.py
runlocal.py
run.py
Время на Linux показывает небольшой выигрыш
настоящие настенные часы. пользователь время в программе. sys - время системных вызовов.
https://docs.python.org/3.5/reference/executionmodel.html#resolution-of-names
источник
читабельность
Помимо производительности при запуске, для локализующих
import
операторов необходимо сделать аргумент читабельности . Например, возьмем строки Python с номерами с 1283 по 1296 в моем текущем первом проекте Python:Если бы
import
выписка была в начале файла, мне пришлось бы пролистать длинный путь или нажать Home, чтобы узнать, чтоET
было. Затем я должен вернуться к строке 1283, чтобы продолжить чтение кода.Действительно, даже если
import
оператор находится в верхней части функции (или класса), как многие его разместят, потребуется подкачка вверх и вниз.Отображение номера версии Gnome будет выполняться редко, поэтому
import
в начале файла вводится ненужная задержка запуска.источник
Я хотел бы упомянуть мой случай использования, очень похожий на упомянутый @John Millikin и @VK:
Дополнительный импорт
Я выполняю анализ данных с помощью Jupyter Notebook и использую тот же блокнот IPython в качестве шаблона для всех анализов. В некоторых случаях мне нужно импортировать Tensorflow, чтобы выполнить несколько быстрых прогонов модели, но иногда я работаю в местах, где не задан тензор потока или импорт выполняется медленно. В этих случаях я инкапсулирую свои зависимые от Tensorflow операции в вспомогательную функцию, импортирую тензорный поток внутри этой функции и привязываю его к кнопке.
Таким образом, я мог бы выполнить команду «перезагрузить и запустить все», не дожидаясь импорта или возобновляя работу остальных ячеек в случае сбоя.
источник
Это увлекательная дискуссия. Как и многие другие, я никогда не рассматривал эту тему. Я был вынужден импортировать функции из-за желания использовать Django ORM в одной из моих библиотек. Мне пришлось позвонить
django.setup()
перед импортом моих классов моделей, и, поскольку это было в начале файла, его перетаскивали в совершенно не код библиотеки Django из-за конструкции инжектора IoC.Я немного взломался и в итоге
django.setup()
поместил конструктор singleton и соответствующий импорт в начало каждого метода класса. Теперь это работало нормально, но меня это беспокоило, потому что импорт не был на вершине, а также я начал беспокоиться о дополнительном времени импорта. Потом я пришел сюда и с большим интересом прочитал, как все это воспринимают.У меня длинный опыт работы с C ++, и теперь я использую Python / Cython. Я считаю, что почему бы не включить импорт в функцию, если это не вызывает профилированного узкого места. Это все равно что объявить пространство для переменных непосредственно перед тем, как они вам понадобятся. Беда в том, что у меня есть тысячи строк кода со всем импортом вверху! Так что я думаю, что я буду делать это с сегодняшнего дня и менять нечетные файлы здесь и там, когда я прохожу и у меня есть время.
источник