Что происходит в Python при импорте внутри функции?

Каковы плюсы и минусы импорта модуля и / или функции Python внутри функции с точки зрения эффективности скорости и памяти?

Выполняется ли повторный импорт каждый раз при запуске функции или, возможно, только один раз в начале, независимо от того, запущена ли функция?


person Tim McJilton    schedule 22.06.2010    source источник
comment
Нет никакого выигрыша в скорости (вызов import очень дорог, даже если модуль уже загружен). Если вы хотите повысить скорость, быстрее (если вы обращаетесь к модулю как минимум 4-5 раз) просто назначьте модуль локальной переменной в качестве первого действия, которое вы делаете в своей функции, а затем обращайтесь к нему через эту локальную переменную. (потому что поиск локальных переменных ОЧЕНЬ быстрый).   -  person Nick Bastin    schedule 22.06.2010
comment
@Nick: При прослушивании этого кажется, что повторный импорт выполняется медленно, поскольку каждый раз, когда вы пытаетесь и проверяете, был ли он импортирован. Вы говорите, что вне функции импортируйте ее и установите как глобальную переменную, а глобальную переменную захватите внутри функции?   -  person Tim McJilton    schedule 23.06.2010
comment
@Tim: Оптимальный способ ускорить доступ к модулю (при условии, что вы пытаетесь это сделать и у вас достаточно доступа к модулю, чтобы сделать локальное назначение целесообразным) - это import модуль на уровне файла, как обычно, а затем внутри функция назначает модуль локальной переменной. Чтобы назначение было целесообразным, вам нужно будет получить доступ к модулю, вероятно, как минимум 4 раза внутри функции - если вы используете модуль реже, чем это, выполнение прямого поиска module.symbol на глобальном уровне не будет медленнее чем локальное присвоение / поиск.   -  person Nick Bastin    schedule 23.06.2010
comment
@Nick: Это чем-то отличается от оптимизации, заключающейся в том, чтобы просто избегать использования точек? IE, если вы вызываете функцию utility.foo () N раз, когда N велико, что лучше выполнить my_foo = utility.foo (), а затем выполнить my_foo () N раз? Где утилита может быть модулем, классом или чем угодно.   -  person Tim McJilton    schedule 24.06.2010
comment
@Tim: Концепция та же, хотя то, что происходит под крышкой, немного отличается между модулями и, скажем, методом экземпляра. Тем не менее, вы действительно должны делать neither из этих вещей в качестве общей практики - вы должны импортировать на уровне модуля и использовать методы / функции в пространстве имен, в котором они в настоящее время находятся, и только в ситуациях с экстремальной производительностью следует думать о микрооптимизациях, таких как переназначение на местного.   -  person Nick Bastin    schedule 24.06.2010
comment
@NickBastin По-прежнему ли так, что назначение модуля локальной переменной является оптимизацией 5 с половиной лет спустя?   -  person 2rs2ts    schedule 12.01.2016
comment
@ 2rs2ts: да - локальные переменные - это самый быстрый доступ, который вы можете получить. Я бы по-прежнему не рекомендовал делать это, если у вас нет цикла с чрезвычайно большим количеством итераций, который требует значительного количества времени.   -  person Nick Bastin    schedule 13.01.2016
comment
@NickBastin Ваша ссылка на pycon не работает.   -  person Navin    schedule 05.05.2018


Ответы (6)


Выполняется ли повторный импорт при каждом запуске функции?

Нет; или, скорее, модули Python кэшируются каждый раз при импорте, поэтому импорт второго (или третьего, или четвертого ...) раза фактически не заставляет их снова проходить весь процесс импорта. 1

Импортируется ли он один раз в начале, независимо от того, запущена ли функция?

Нет, он импортируется только тогда, когда функция выполняется. 2, 3

Что касается преимуществ: это зависит от ситуации. Если вы можете запускать функцию очень редко и вам не нужно импортировать модуль где-либо еще, может быть полезным импортировать его только в этой функции. Или, если есть конфликт имен или другая причина, по которой вы не хотите, чтобы модуль или символы из модуля были доступны везде, вы можете импортировать их только в определенной функции. (Конечно, для таких случаев всегда есть from my_module import my_function as f.)

В общей практике это, наверное, не так уж и полезно. Фактически, большинство руководств по стилю Python побуждают программистов помещать все операции импорта в начало файла модуля.

person mipadi    schedule 22.06.2010
comment
По той же мысли, это может сделать зависимость необязательной, если импорт находится внутри вспомогательной функции. - person M. Toya; 24.03.2015
comment
Я использую его для дополнительных зависимостей, когда пишу для себя библиотечные модули. Я делаю каждую функцию внутри библиотечного модуля зависимой от минимального количества импортов. - person CodeMonkey; 01.07.2016
comment
Благодарность! он сэкономил моему приложению web2py много времени загрузки, поместив медленный модуль import plotly внутрь функции, из которой он вызывается. - person laviex; 04.02.2019
comment
Я обнаружил, что иногда мне нужен пакет A для конкретной задачи. Позже я переписываю код и решаю использовать вместо него пакет B. На этом этапе я могу не вспомнить, использовался ли пакет A где-то еще в моем проекте, и могу либо не удалить его, пока он больше не нужен, либо удалить его, пока он все еще нужен (чего я не замечу сразу, если это редко используемая часть кода). Интересно, есть ли что-то, что делают другие, чтобы избежать такой проблемы, когда вы питонически не импортируете внутренние функции? - person PatrickT; 14.05.2020

В самый первый раз, когда вы import goo из любого места (внутри или вне функции), загружается goo.py (или другая импортируемая форма), и sys.modules['goo'] устанавливается в созданный таким образом объект модуля. Любой будущий импорт в рамках того же запуска программы (опять же, внутри или вне функции) просто найдите sys.modules['goo'] и привяжите его к barename goo в соответствующей области. Поиск dict и привязка имени - очень быстрые операции.

Предполагая, что самый первый import в любом случае полностью амортизируется за время выполнения программы, наличие «соответствующей области» на уровне модуля означает, что каждое использование goo.this, goo.that и т. Д. Представляет собой два поиска dict - один для goo и один для имени атрибута. Если он находится на «функциональном уровне», то при каждом запуске функции оплачивается одна дополнительная установка локальной переменной (даже быстрее, чем при поиске по словарю!), Но сохраняется один поиск по словарю (замена его на поиск по локальной переменной, невероятно быстро) для каждого goo.this (и т. д.), что в основном сокращает время поиска вдвое.

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

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

person Alex Martelli    schedule 22.06.2010
comment
Эта оптимизация на самом деле никогда не имеет смысла - никакой быстрый доступ к локальной переменной не компенсирует невероятных затрат на вызов import, даже если модуль уже загружен. Проверка того, был ли загружен модуль, - очень дорогостоящая операция (по сравнению с несколькими поисками по глобальному словарю). - person Nick Bastin; 22.06.2010
comment
Импорт модуля в первый раз стоит дорого. Попробуйте запустить пустой сценарий вместо сценария, содержащего только import string,itertools,fractions,heapq,re,array,bisect,collections,math,os. Первый занимает в среднем 180 мс, а второй 230 мс. Так что для начала это не микросекунды. Это десятки миллисекунд (может, происходит доступ к диску?). Это важно для крошечных скриптов, которые выполняются много раз (например, для обслуживания веб-запросов). - person Evgeni Sergeev; 26.03.2016
comment
@EvgeniSergeev В этом случае у вас обычно есть сервер, работающий все время, поэтому он не будет повторно импортировать снова и снова - person Tobias Kienzler; 24.08.2016
comment
@TobiasKienzler он по-прежнему применяется для среды FaaS (функция как услуга) и / или для некоторых низкопроизводительных (например, встроенных) устройств. Аргумент в том, что разница в задержке может быть значительной и ею нельзя просто пренебречь. - person user3576508; 28.11.2017

Он импортируется один раз при первом выполнении функции.

Плюсы:

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

Минусы:

  • не видно, от каких модулей может зависеть этот модуль
person SilentGhost    schedule 22.06.2010
comment
если вы сделаете что-то вроде grep import /path/to/module, он покажет вам все импортируемые модули. - person ; 23.12.2016

Могу я предложить в целом вместо вопроса: «Сможет ли X улучшить мою производительность?» вы используете профилирование, чтобы определить, на что ваша программа фактически тратит свое время, а затем применяете оптимизацию в зависимости от того, где вы получите наибольшую выгоду?

А затем вы можете использовать профилирование, чтобы убедиться, что ваша оптимизация действительно принесла вам пользу.

person gomad    schedule 22.06.2010
comment
Я согласен с этим, это был скорее вопрос из любопытства. Мне было интересно, как работает метод импорта Python, более подробно, чем о преждевременном повышении производительности. Спасибо хоть :) - person Tim McJilton; 23.06.2010
comment
Ах. Что ж, надеюсь, прекрасные ответы удовлетворили ваше любопытство! У Effbot есть некоторая информация, которая может быть вам полезна: effbot.org/zone/import-confusion. htm Прокрутите вниз до раздела "Что делает Python для импорта модуля?" - person gomad; 23.06.2010
comment
Спасибо за информацию, ответы были очень хорошими и помогли. - person Tim McJilton; 23.06.2010

Импорт внутри функции фактически импортирует модуль один раз .. при первом запуске функции.

Он должен импортировать так же быстро, независимо от того, импортируете ли вы его вверху или при запуске функции. Обычно это не хорошая причина для импорта в def. Плюсы? Он не будет импортирован, если функция не вызывается ... Это действительно разумная причина, если ваш модуль требует, чтобы у пользователя был установлен только определенный модуль, если они используют ваши определенные функции ...

Если вы делаете это не по этой причине, то это почти наверняка неприятная идея.

person royal    schedule 22.06.2010

Он импортируется один раз при первом вызове функции.

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

person Tim Pietzcker    schedule 22.06.2010