Использовать '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 3
Как импортировать класс в пределах той же директории или поддиректории?
Импорт модуля по относительному пути