Локальные операторы импорта в Python

Я думаю, что приведение оператора import близко к фрагменту, который использует его, помогает читаемости, делая свои зависимости более ясными. Будет ли Python кэшировать это? Мне все равно? Это плохая идея?

def Process():
    import StringIO
    file_handle=StringIO.StringIO('hello world')
    #do more stuff

for i in xrange(10): Process()

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

Ответ 1

Другие ответы проявляют легкую путаницу в отношении того, как import действительно работает.

Это утверждение:

import foo

примерно эквивалентен этому утверждению:

foo = __import__('foo', globals(), locals(), [], -1)

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

Обработчик функции __import__() концептуально преобразует строку ('foo') в объект модуля. Модули кэшируются в sys.modules и что первое место __import__() выглядит - если sys.modules имеет запись для 'foo', то что __import__('foo') вернется, что бы это ни было. Это действительно не волнует тип. Вы можете видеть это в действии самостоятельно; попробуйте запустить следующий код:

import sys
sys.modules['boop'] = (1, 2, 3)
import boop
print boop

Оставляя в стороне стилистические проблемы на данный момент, наличие инструкции импорта внутри функции работает так, как вам хотелось бы. Если модуль ранее не импортировался, он импортируется и кэшируется в sys.modules. Затем он назначает модуль локальной переменной с этим именем. Он не изменяет никакого состояния на уровне модуля. Возможно, он изменит какое-то глобальное состояние (добавив новую запись в sys.modules).

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

Ответ 2

См. PEP 8:

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

Обратите внимание, что это чисто стилистический выбор, поскольку Python будет обрабатывать все операторы import одинаково независимо от того, где они объявлены в исходном файле. Тем не менее я бы рекомендовал вам следовать обычной практике, так как это сделает ваш код более читаемым для других.

Ответ 3

Стиль в стороне, правда, импортированный модуль будет импортироваться только один раз (если reload не вызван в указанном модуле). Однако каждый вызов import Foo неявно проверяет, загружен ли этот модуль (проверив sys.modules).

Рассмотрим также "дизассемблирование" двух других равных функций, в которых один пытается импортировать модуль, а другой - не:

>>> def Foo():
...     import random
...     return random.randint(1,100)
... 
>>> dis.dis(Foo)
  2           0 LOAD_CONST               1 (-1)
              3 LOAD_CONST               0 (None)
              6 IMPORT_NAME              0 (random)
              9 STORE_FAST               0 (random)

  3          12 LOAD_FAST                0 (random)
             15 LOAD_ATTR                1 (randint)
             18 LOAD_CONST               2 (1)
             21 LOAD_CONST               3 (100)
             24 CALL_FUNCTION            2
             27 RETURN_VALUE        
>>> def Bar():
...     return random.randint(1,100)
... 
>>> dis.dis(Bar)
  2           0 LOAD_GLOBAL              0 (random)
              3 LOAD_ATTR                1 (randint)
              6 LOAD_CONST               1 (1)
              9 LOAD_CONST               2 (100)
             12 CALL_FUNCTION            2
             15 RETURN_VALUE        

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

Быстрый и грязный тест timeit показывает небольшое улучшение скорости при использовании Bar:

$ python -m timeit -s "from a import Foo,Bar" -n 200000 "Foo()"
200000 loops, best of 3: 10.3 usec per loop
$ python -m timeit -s "from a import Foo,Bar" -n 200000 "Bar()"
200000 loops, best of 3: 6.45 usec per loop

Ответ 4

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

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

Кроме того, это нарушает то, что я считаю довольно важным принципом: прямо не изменяйте состояние уровня модуля внутри функции.

Edit:

Собственно, оказывается, что все вышеперечисленное - глупость.

Импорт модуля не изменяет состояние на уровне модуля (он инициализирует импортируемый модуль, если ничего еще не существует, но это совсем не то же самое). Импорт модуля, который вы уже импортировали в другое место, не требует ничего, кроме поиска sys.modules и создания переменной в локальной области.

Зная это, я чувствую себя глупо, фиксируя все места в моем коде, где я его исправил, но чтобы мой крест нести.

Ответ 5

Когда интерпретатор Python попадает в оператор импорта, он начинает считывать все определения функций в импортируемом файле. Это объясняет, почему иногда импорт может занять некоторое время.

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

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

Ответ 6

Я вижу два способа, когда вам нужно импортировать его локально

  • Для целей тестирования или для временного использования вам нужно что-то импортировать, в этом случае вы должны поместить импорт в место использования.

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

В противном случае всегда ставьте его сверху для эффективности и согласованности.