Использовать 'import module' или 'from module import'?
Я пытался найти исчерпывающее руководство о том, что лучше использовать: import module или from module import. Я только начинаю изучать Python и стараюсь следовать лучшим практикам с самого начала.
В общем, мне было бы интересно узнать о вашем опыте, какие предпочтения есть у других разработчиков и какой самый надежный способ избежать возможных подводных камней в будущем?
5 ответ(ов)
Разница между import module и from module import foo в основном субъективна. Выберите тот способ, который вам больше нравится, и будьте последовательны в его использовании. Вот несколько пунктов, которые могут помочь вам определиться.
import module
- Плюсы:
- Меньше забот о ваших
import-заявлениях. Не нужно добавлять дополнительные импорты, чтобы начать использовать другой элемент из модуля.
- Меньше забот о ваших
- Минусы:
- Набор
module.fooв вашем коде может быть утомительным и избыточным (утомление можно минимизировать, используяimport module as mo, и тогда нужно будет писатьmo.foo).
- Набор
from module import foo
- Плюсы:
- Меньше набора символов для использования
foo. - Больше контроля над тем, какие элементы модуля доступны.
- Меньше набора символов для использования
- Минусы:
- Чтобы использовать новый элемент из модуля, вам нужно обновить
import-заявление. - Вы теряете контекст относительно
foo. Например, менее понятно, что делаетceil(), по сравнению сmath.ceil().
- Чтобы использовать новый элемент из модуля, вам нужно обновить
Оба метода приемлемы, но не используйте from module import *.
Для любого разумно большого объема кода, если вы используете import *, вы, вероятно, закрепите его в модуле, и будет трудно его удалить. Это связано с тем, что сложно определить, какие элементы используются в коде и откуда они взяты из 'module', что делает ситуацию опасной: вы можете прийти к выводу, что больше не используете этот import, но на самом деле это очень трудно проверить.
Есть еще один момент, который не упомянут, но он связан с записью в модуль. Хотя это может быть не очень распространенным, мне это время от времени было необходимо.
Из-за того, как в Python работают ссылки и связывание имен, если вы хотите обновить какой-либо символ в модуле, скажем, foo.bar, из вне этого модуля и хотите, чтобы другой импортирующий код "увидел" это изменение, вам нужно импортировать foo определенным образом. Например:
модуль foo:
bar = "яблоки"
модуль a:
import foo
foo.bar = "апельсины" # обновляем bar внутри объекта модуля foo
модуль b:
import foo
print(foo.bar) # если выполнен после присваивания "foo.bar" в a, выведет "апельсины"
Однако, если вы импортируете имена символов вместо имен модулей, это не сработает.
Например, если я сделаю это в модуле a:
from foo import bar
bar = "апельсины"
Никакой код вне a не увидит bar как "апельсины", потому что мое присвоение bar лишь затрагивает имя "bar" внутри модуля a, и оно не "заглядывает" внутрь объекта модуля foo и не обновляет его bar.
Хотя многие уже объяснили различия между import и from import, я постараюсь подробнее рассказать о том, что происходит "под капотом" и в каких местах происходят изменения.
import foo:
Этот синтаксис импортирует модуль foo и создает ссылку на этот модуль в текущем пространстве имен. Чтобы получить доступ к определенному атрибуту или методу из этого модуля, вам нужно указать полный путь к нему.
Например: foo.bar, но не просто bar.
from foo import bar:
Этот синтаксис также импортирует модуль foo, но создает ссылки только для указанных членов (bar). Переменная foo при этом не устанавливается.
Например: bar, но не baz или foo.baz.
from foo import *:
Этот вариант импортирует модуль foo и создает ссылки на все публичные объекты, определенные в этом модуле, в текущем пространстве имен (все, что указано в __all__, если оно существует, иначе все объекты, которые не начинаются с _). Переменная foo при этом также не устанавливается.
Например: bar и baz, но не _qux или foo._qux.
Теперь рассмотрим, что происходит, когда мы выполняем import X.Y:
>>> import sys
>>> import os.path
Проверим sys.modules на предмет наличия os и os.path:
>>> sys.modules['os']
<module 'os' from '/Path/to/os.pyc'>
>>> sys.modules['os.path']
<module 'posixpath' from '/Path/to/posixpath.pyc'>
Проверим globals() и locals() на наличие os и os.path:
>>> globals()['os']
<module 'os' from '/Path/to/os.pyc'>
>>> locals()['os']
<module 'os' from '/Path/to/os.pyc'>
>>> globals()['os.path']
Traceback (most recent call last):
...
KeyError: 'os.path'
Из приведенного выше примера видно, что только os добавлен в локальное и глобальное пространство имен.
Таким образом, мы можем использовать:
>>> os
<module 'os' from '/Path/to/os.pyc'>
>>> os.path
<module 'posixpath' from '/Path/to/posixpath.pyc'>
Но не можем использовать просто path.
>>> path
Traceback (most recent call last):
...
NameError: name 'path' is not defined
После удаления os из локального пространства имен, вы не сможете получить доступ ни к os, ни к os.path, даже если они существуют в sys.modules:
>>> del locals()['os']
>>> os
Traceback (most recent call last):
...
NameError: name 'os' is not defined
>>> os.path
Traceback (most recent call last):
...
NameError: name 'os' is not defined
Теперь поговорим про import from:
from:
>>> import sys
>>> from os import path
Проверим sys.modules на наличие os и os.path:
>>> sys.modules['os']
<module 'os' from '/Path/to/os.pyc'>
>>> sys.modules['os.path']
<module 'posixpath' from '/Path/to/posixpath.pyc'>
Мы видим, что в sys.modules все так же, как и раньше с использованием import name.
Теперь проверим, как это выглядит в пространствах имен locals() и globals():
>>> globals()['path']
<module 'posixpath' from '/Path/to/posixpath.pyc'>
>>> locals()['path']
<module 'posixpath' from '/Path/to/posixpath.pyc'>
>>> globals()['os']
Traceback (most recent call last):
...
KeyError: 'os'
Мы можем использовать path, но не можем обратиться к os.path:
>>> path
<module 'posixpath' from '/Path/to/posixpath.pyc'>
>>> os.path
Traceback (most recent call last):
...
NameError: name 'os' is not defined
Удалим path из locals():
>>> del locals()['path']
>>> path
Traceback (most recent call last):
...
NameError: name 'path' is not defined
И пример с использованием алиаса:
>>> from os import path as HELL_BOY
>>> locals()['HELL_BOY']
<module 'posixpath' from '/Path/to/posixpath.pyc'>
>>> globals()['HELL_BOY']
<module 'posixpath' from '/Path/to/posixpath.pyc'>
И без path в пространстве имен:
>>> globals()['path']
Traceback (most recent call last):
...
KeyError: 'path'
Оба способа импорта поддерживаются и имеют свои причины: в некоторых случаях один способ оказывается более подходящим, чем другой.
import module: это удобно, когда вы используете много элементов из модуля. Недостаток в том, что вам потребуется уточнять каждую ссылку с помощью имени модуля.from module import ...: преимуществом является то, что импортированные элементы можно использовать напрямую без префикса имени модуля. Однако недостаток в том, что вам нужно перечислять все используемые элементы, и становится неясно, откуда что взялось в коде.
Выбор зависит от того, что делает код более понятным и читаемым, и во многом зависит от личных предпочтений. Я обычно предпочитаю import module, так как в коде очень ясно, откуда берется объект или функция. Я использую from module import ..., когда часто использую какой-то объект или функцию в коде.
Я всегда использую следующую конструкцию:
from package.subpackage.subsubpackage import module
А затем обращаюсь ко всему как:
module.function
module.modulevar
Причина этого подхода в том, что у вас получается краткий вызов, и вы четко определяете пространство имен модуля для каждой функции, что очень удобно, если вам нужно искать использование конкретного модуля в вашем коде.
Не стоит забывать, что не следует использовать from module import *, так как это загрязняет пространство имен и не позволяет понять, откуда берется та или иная функция (из какого модуля).
Разумеется, могут возникнуть проблемы, если у вас одинаковые имена модулей в разных пакетах, например:
from package1.subpackage import module
from package2.subpackage import module
В этом случае, конечно, вы столкнетесь с проблемами, но это явный сигнал о том, что ваша структура пакетов нуждается в переосмыслении.
Как разгрузить (перезагрузить) модуль Python?
Как импортировать другие файлы Python?
Импорт модуля по относительному пути
Как решить проблему "ImportError: Невозможно импортировать имя X" или "AttributeError: ... (вероятно, из-за циклического импорта)"?
Подключение файла из подпапки?