Это питон, чтобы импортировать внутри функции? - PullRequest
104 голосов
/ 21 июня 2009

PEP 8 говорит:

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

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

Есть мнения?

РЕДАКТИРОВАТЬ (причина, по которой мне кажется, что импорт функций может быть хорошей идеей):

Основная причина: это может сделать код более понятным.

  • Когда я смотрю на код функции, я могу спросить себя: «Что такое функция / класс xxx?» (ххх используется внутри функции). Если у меня есть все мои импорта в верхней части модуля, я должен пойти посмотреть, чтобы определить, что такое ххх. Это больше проблема при использовании from m import xxx. Видя m.xxx в функции, вероятно, говорит мне больше. В зависимости от того, что является m: Это хорошо известный модуль / пакет верхнего уровня (import m)? Или это субмодуль / пакет (from a.b.c import m)?
  • В некоторых случаях наличие этой дополнительной информации («Что такое ххх?») Рядом с местом, где используется ххх, может облегчить понимание функции.

Ответы [ 8 ]

71 голосов
/ 22 июня 2009

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

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

Еще один момент, я предпочитаю получить исключение ImportError до запуска любого кода - в качестве проверки работоспособности, так что это еще одна причина для импорта вверху.

Я использую pyChecker для проверки неиспользуемых модулей.

37 голосов
/ 21 июня 2009

Есть два случая, когда я нарушаю PEP 8 в этом отношении:

  • Круговой импорт: модуль A импортирует модуль B, но что-то в модуле B нуждается в модуле A (хотя это часто является признаком того, что мне нужно реорганизовать модули для устранения циклической зависимости)
  • Вставка точки останова pdb: import pdb; pdb.set_trace() Это удобно, т.к. я не хочу ставить import pdb в верхней части каждого модуля, который я хочу отлаживать, и легко забыть удалить импорт, когда я удалить точку останова.

За пределами этих двух случаев хорошая идея поставить все наверх. Это делает зависимости более ясными.

19 голосов
/ 21 июня 2009

Вот четыре варианта использования импорта, которые мы используем

  1. importfrom x import y и import x as y) вверху

  2. Выбор для импорта. Вверху.

    import settings
    if setting.something:
        import this as foo
    else:
        import that as foo
    
  3. Условный импорт. Используется с JSON, XML-библиотеками и тому подобным. Вверху.

    try:
        import this as foo
    except ImportError:
        import that as foo
    
  4. Динамический импорт. Пока у нас есть только один пример этого.

    import settings
    module_stuff = {}
    module= __import__( settings.some_module, module_stuff )
    x = module_stuff['x']
    

    Обратите внимание, что этот динамический импорт не вносит код, но приносит структуры данных, написанные на Python. Это как маринованный кусок данных за исключением того, что мы мариновали его вручную.

    Это также, более или менее, вверху модуля


Вот что мы делаем, чтобы сделать код более понятным:

  • Держите модули короткими.

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

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

7 голосов
/ 21 июня 2009

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

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

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

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

FWIW, есть случаи, когда имеет смысл импортировать данные внутри функции. Например, если вы хотите установить язык в cx_Oracle, вам нужно установить переменную среды NLS _ LANG до того, как будет импортирован. Таким образом, вы можете увидеть такой код:

import os

oracle = None

def InitializeOracle(lang):
    global oracle
    os.environ['NLS_LANG'] = lang
    import cx_Oracle
    oracle = cx_Oracle
6 голосов
/ 21 июня 2009

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

4 голосов
/ 27 сентября 2017

Исходя из вопроса о двойной загрузке модуля - Почему не оба?

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

2 голосов
/ 21 июня 2009

Пока это import, а не from x import *, вы должны поместить их сверху. Он добавляет только одно имя в глобальное пространство имен, и вы придерживаетесь PEP 8. Кроме того, если вам позже понадобится это где-то еще, вам не нужно ничего перемещать.

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

1 голос
/ 26 марта 2019

Посмотрите на альтернативный подход, который используется в sqlalchemy: внедрение зависимостей:

@util.dependencies("sqlalchemy.orm.query")
def merge_result(query, *args):
    #...
    query.Query(...)

Обратите внимание, как импортированная библиотека объявляется в декораторе и передается в качестве аргумента функции !

Этот подход делает код чище, а также работает в 4,5 раза быстрее , чем оператор import!

Тест: https://gist.github.com/kolypto/589e84fbcfb6312532658df2fabdb796

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...