6

Использовать 'import module' или 'from module import'?

1

Я пытался найти исчерпывающее руководство о том, что лучше использовать: import module или from module import. Я только начинаю изучать Python и стараюсь следовать лучшим практикам с самого начала.

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

5 ответ(ов)

6

Разница между 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, но на самом деле это очень трудно проверить.

2

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

Из-за того, как в 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.

1

Хотя многие уже объяснили различия между 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'
0

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

  • import module: это удобно, когда вы используете много элементов из модуля. Недостаток в том, что вам потребуется уточнять каждую ссылку с помощью имени модуля.
  • from module import ...: преимуществом является то, что импортированные элементы можно использовать напрямую без префикса имени модуля. Однако недостаток в том, что вам нужно перечислять все используемые элементы, и становится неясно, откуда что взялось в коде.

Выбор зависит от того, что делает код более понятным и читаемым, и во многом зависит от личных предпочтений. Я обычно предпочитаю import module, так как в коде очень ясно, откуда берется объект или функция. Я использую from module import ..., когда часто использую какой-то объект или функцию в коде.

0

Я всегда использую следующую конструкцию:

from package.subpackage.subsubpackage import module

А затем обращаюсь ко всему как:

module.function
module.modulevar

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

Не стоит забывать, что не следует использовать from module import *, так как это загрязняет пространство имен и не позволяет понять, откуда берется та или иная функция (из какого модуля).

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

from package1.subpackage import module
from package2.subpackage import module

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь