Это Python для импорта внутренних функций?

PEP 8 говорит:

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

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

Есть мнения?

ИЗМЕНИТЬ (причина, по которой я считаю, что импорт функций может быть хорошей идеей):

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

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

person codeape    schedule 21.06.2009    source источник
comment
а вы это делаете для производительности?   -  person Macarse    schedule 21.06.2009
comment
Я чувствую, что в некоторых случаях это делает код более понятным. Я бы предположил, что необработанная производительность падает при импорте в функцию (поскольку оператор импорта будет выполняться каждый раз при вызове функции).   -  person codeape    schedule 21.06.2009
comment
Вы можете ответить Что такое функция / класс xxx? используя синтаксис импорта xyz, а не синтаксис импорта abc from xyz   -  person Tom Leys    schedule 22.06.2009
comment
Если ясность является единственным фактором, U может также включить соответствующий комментарий на этот счет. ;)   -  person lprsd    schedule 23.06.2009
comment
@becomingGuru: Конечно, но комментарии могут не совпадать с реальностью ...   -  person codeape    schedule 20.10.2009
comment
@codeape: так зачем вообще комментировать код, верно? Теперь я знаю, почему комментарии все реже и реже ... ;-)   -  person Jürgen A. Erhard    schedule 26.12.2009
comment
Связанный вопрос: Что происходит в python, когда вы импортируете внутри функции?.   -  person Cristian Ciupitu    schedule 04.04.2014
comment
Мне это не кажется питоническим ... Однако: при использовании функций Azure кажется, что переменные, определенные в настройках приложения или local_settings.json, недоступны до тех пор, пока не будет вызвана основная функция, поэтому любой модуль (настройки), который пытается их использовать, должен быть импортированным внутри. Еще один случай, когда правило может быть нарушено.   -  person ericson.cepeda    schedule 01.02.2019
comment
У меня тот же вопрос, но совсем по другой причине. Скажем, я хочу сделать разные функции для импорта и обработки разных больших наборов данных. Их одновременный импорт всех в начало файла вызовет множество ненужных операций импорта и займет много времени для загрузки, поскольку, возможно, требуется использовать только один набор данных. В этом случае, что не так с импортом внутри функции, которая вызывается для этого конкретного набора данных?   -  person user4779    schedule 25.04.2019
comment
Возможный дубликат Если операторы импорта всегда находятся в в верхней части модуля?   -  person John Y    schedule 20.09.2019
comment
https://docs.python.org/3/reference/import.html#loaders: Если модуль является модулем Python ... загрузчик должен выполнить код модуля в глобальном пространстве имен модуля ...   -  person noobninja    schedule 05.06.2020
comment
Я не видел однозначного ответа. Самый большой вопрос - зачем импортировать (вверху) модель, которую нельзя использовать? В моем случае я использую вложенный if-elif для выбора одной из нескольких моделей.   -  person Mohd    schedule 02.10.2020


Ответы (10)


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

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

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

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

person Peter Ericson    schedule 22.06.2009
comment
Спасибо за подсказку ImportError, я никогда не думал об этом! - person Aubergine; 12.01.2021

Я нарушаю PEP 8 в двух случаях:

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

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

person Rick Copeland    schedule 21.06.2009
comment
Я согласен, что это делает зависимости более понятными в отношении модуля в целом. Но я считаю, что это может сделать код менее понятным на уровне функций, чтобы импортировать все, что находится наверху. Когда вы смотрите на код функции, вы можете спросить себя: что такое функция / класс xxx? (xxx используется внутри функции). И вы должны посмотреть на самую верхнюю часть файла, чтобы увидеть, откуда берется xxx. Это больше проблема при использовании from m import xxx. Просмотр m.xxx говорит вам больше - по крайней мере, если нет никаких сомнений относительно того, что такое m. - person codeape; 21.06.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. Это что-то вроде маринованного фрагмента данных, за исключением того, что мы собирали его вручную.

    Это также более или менее находится в верхней части модуля.


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

  • Модули должны быть короткими.

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

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

person S.Lott    schedule 21.06.2009
comment
Использование коротких модулей, конечно, очень хорошая идея. Но чтобы всегда иметь доступ к информации об импорте для функций, максимальная длина модуля должна составлять один экран (возможно, максимум 100 строк). И это, вероятно, было бы слишком коротким, чтобы быть практичным в большинстве случаев. - person codeape; 22.06.2009
comment
Я полагаю, вы могли бы довести это до крайности. Я думаю, что может быть точка баланса, когда ваш модуль достаточно мал, чтобы вам не понадобились причудливые методы импорта для управления сложностью. Наш средний размер модуля - по совпадению - около 100 строк. - person S.Lott; 22.06.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
person Jason Baker    schedule 21.06.2009
comment
Я согласен с вашей проблемой обслуживания. Рефакторинг кода может быть немного проблематичным. Если я добавляю вторую функцию, которая использует модуль, ранее использовавшийся только одной функцией, я либо перемещаю импорт наверх, либо нарушаю собственное общее правило, импортируя модуль также во вторую функцию. - person codeape; 21.06.2009
comment
Я думаю, что аргумент о производительности может идти и по другому пути. Импорт модуля может занять много времени. В распределенных файловых системах, таких как суперкомпьютеры, импорт большого модуля, такого как numpy, может занять несколько секунд. Если модуль нужен только для одной, редко используемой функции, то импорт в функцию значительно ускорит общий случай. - person amaurea; 11.10.2016

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

person Dan Lew    schedule 21.06.2009

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

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

person IljaBek    schedule 27.09.2017

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

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

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

Такой подход делает код чище, а также работает в 4,5 раза быстрее, чем инструкция import!

Контрольный показатель: https://gist.github.com/kolypto/589e84fbcfb6312532658df2f2

person kolypto    schedule 26.03.2019

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

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

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

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

person ZioByte    schedule 14.07.2020

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

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

person Javier    schedule 21.06.2009
comment
Фактически, добавление from x import * внутрь функции вызовет SyntaxWarning, по крайней мере, в 2.5. - person Rick Copeland; 21.06.2009

В модулях, которые являются «обычными» модулями и могут быть выполнены (т.е. имеют if __name__ == '__main__':-section), я обычно импортирую модули, которые используются только при выполнении модуля внутри основного раздела.

Пример:

def really_useful_function(data):
    ...


def main():
    from pathlib import Path
    from argparse import ArgumentParser
    from dataloader import load_data_from_directory

    parser = ArgumentParser()
    parser.add_argument('directory')
    args = parser.parse_args()
    data = load_data_from_directory(Path(args.directory))
    print(really_useful_function(data)


if __name__ == '__main__':
    main()
person codeape    schedule 13.09.2019