Перевод статьи The Definitive Guide to Python import Statements.
Я почти никогда не мог с первого раза написать правильно такие инструкции Python как import
. Поведение этой инструкции не одинаково в различных версиях Python 2.7 и Python 3.6 (эти две версии рассматриваются здесь) и нет единого способа сделать так, что бы инструкция импорта import
всегда гарантированно работала так как нам нужно. Эта статья — погружение в решение проблем с использованием инструкции import
.
Содержание
- Общее / Ключевые моменты
- Основные определения
- Пример структуры директории
- Что такое import ?
- Основы использования оператора import и переменной sys.path
- Подробнее о sys.path
- Все о __init__.py
- Использование объектов из импортированного модуля или пакета
- Импорт с использованием абсолютных и относительных путей
- Примеры
- Python 2 VS. Python 3
Общее / Ключевые моменты
- инструкция
import
осуществляет поиск модулей в списке путей, содержащемся в переменнойsys.path
; -
sys.path
всегда включает путь к скрипту, запущенному в настоящий момент времени в командной строке и однозначно задает рабочую директорию для текущего сеанса командной строки; - концептуально импорт пакета происходит, как импорт файла пакета
__init__.py
.
Основные определения
-
модуль: любой файл с расширением
*.py
. Именем модуля является имя файла. -
встроенный модуль: модуль (написанный на С ), который интегрирован непосредственно в интерпретатор Python и следовательно не имеет отдельного файла с расширением
*.py
. -
пакет: любая папка содержащая файл с именем
__init\__.py
. Имя пакета совпадает с именем папки. В Python 3.3 и выше любая папка (даже без файла__init\__.py
) считается пакетом. - объект: в Python почти всё является объектом: функции, классы, переменные и т.д.
Пример структуры директории
test/ # корневая папка packA/ # пакет packA subA/ # субпакет subA __init__.py sa1.py sa2.py __init__.py a1.py a2.py packB/ # пакет packB (неявное пространство имен пакета) b1.py b2.py math.py random.py other.py start.py
Обратите внимание, что мы не размещаем __init\__.py
в корневой папке test/
.
Что такое import ?
Когда вы импортируете модуль, Python запускает код из файла модуля. Когда вы импортируете пакет, Python сначала запускает код из файла пакета __init__.py
, если такой файл существует. После этого все объекты, определенные в файле модуля или __init__.py
пакета, становятся доступны.
Основы использования оператора import и переменной sys.path
В соответствии с документацией Python, оператор import
ищет корректный модуль или пакет для импорта в соответствии со следующими правилами.
Когда импортируется модуль с определенным именем, например
spam
, интерпретатор сначала ищет встроенный модуль с этим именем.
Если он не найден, он ищет файл с именемspam.py
в списке каталогов, заданных в переменнойsys.path
.sys.path
инициализируется следующими значениями путей:
- директория, содержащая запущенный на исполнение скрипт (или текущий каталог, когда не указан файл).
- переменная окружения PYTHONPATH (список имен каталогов с тем же синтаксисом, что и PATH переменная оболочки
shell
).- значение по умолчанию, заданное при установке Python.
После запуска программы, написанные на Python, могут изменять значение переменной
sys.path
. Каталог, содержащий выполняемый скрипт, помещается в начало списка путей для поиска при импорте, впереди значения, содержащего путь к стандартной библиотеке.
Технически документация Python является неполной. На самом деле интерпретатор ищет не только файл (то есть, модуль) с именем spam.py
, также он будет искать папку (то есть, пакет) с именем spam
.
Обратите внимание, что интерпретатор Python сначала ищет список встроенных модулей, то есть модулей, которые интегрированы непосредственно в интерпретатор Python. Состав списка встроенных модулей зависит от типа установки и может быть найден в файлах sys.builtin_module_names
(для Python версии 2 и 3). Обычно встроенные модули, которые входят с состав начальной установки, включают в себя sys
(всегда устанавливается), math
, itertools
и time
и другие.
В отличие от встроенных модулей (модулей стандартной библиотеки), пути к которым помещаются первыми в списке для поиска при импорте остальные модули в стандартной библиотеке Python появляются после пути каталога текущего скрипта. Это приводит к запутанному поведению: становится возможно «заменить» некоторые, но не все модули в стандартной библиотеке Python.
Например, на моем компьютере (Windows 10, Python 3.6) модуль math
является встроенным модулем, тогда как модуль random
нет. Таким образом, import math
в start.py
будет импортировать модуль math
из стандартной библиотеки, а не мой собственный файл math.py
, находящийся в тот же каталоге.
А оператор import random
в start.py
будет импортировать мой файл random.py
, а не модуль random
из стандартной библиотеки.
Кроме того, оператор import
чувствителен к регистру символов в названии импортируемого файла. import Spam
— это не то же самое, что import spam
.
Функция pkgutil.iter_modules
(Python 2 и 3) может использоваться для получения списка всех импортируемых модулей по заданному пути:
import pkgutil # установите значение переменной search_path равным None, чтобы увидеть все модули, импортируемые из sys.path search_path = '.' all_modules = [x[1] for x in pkgutil.iter_modules(path=search_path)] print(all_modules)
Источники:
Как получить список встроенных модулей в python ?
Подробнее о sys.path
Чтобы узнать, что находится в переменной sys.path
, запустите в командной строке следующие команды или скрипт содержащий инструкции:
import sys print(sys.path)
Документация Python для sys.path
описывает это следующим образом.
Список строк, определяющий пути поиска для модулей, инициализируется из переменной среды PYTHONPATH, а также зависит от значений по умолчанию при установке.
В ходе инициализации при запуске программы Python, первым элементом этого спискаpath[0]
, будет являться каталог, содержащий скрипт, который использовался при вызове интерпретатора Python.
Если каталог содержащий файл сценария недоступен (например, если интерпретатор работает в интерактивном режиме или если скрипт считывается в потоке стандартного ввода),path[0]
— будет задан пустой строкой, которая направляет Python на поиск модулей в текущем каталоге.
Обратите внимание, что путь к каталогу, содержащему файл сценария будет вставлен перед путями полученными из переменной PYTHONPATH.
Источник: Python 2 и 3
Документация для интерфейса командной строки Python добавляет следующее о запуске сценариев из командной строки. В частности, при запуске командыpython script.py
, происходит следующее.
Если имя сценария ссылается непосредственно на файл с расширением
py
, то каталог, содержащий этот файл, добавляется в началоsys.path
и файл выполняется в качестве основного модуля.
Источник: Python 2 и 3
Давайте рассмотрим порядок, в соответствии с которым интерпретатор Python ищет модули для импорта:
- Модули в стандартной библиотеке Python (например,
math
,os
). - Модули или пакеты в каталоге, заданном в sys.path:
- Если интерпретатор Python запускается в интерактивном режиме:
-
sys.path[0]
— пустая строка ‘ ‘. Это служит указанием для интерпретатора Python, что в качестве текущего рабочего каталога необходимо использовать каталог из которого был запущен интерпретатор, т. е. результат возвращаемый утилитойpwd
в операционных системах Unix.
-
- Если мы запускаем сценарий из командной строки с использованием команды вида
python script.py
:-
sys.path[0]
— записывается как путь кscript.py
.
-
- Если интерпретатор Python запускается в интерактивном режиме:
- Каталоги в переменной окружения PYTHONPATH
- Значения из переменной
sys.path
, заданные по умолчанию.
Обратите внимание, что при запуске скрипта Python в sys.path
не учитывается, каков ваш текущий «рабочий каталог». Учитывается только о путь к сценарию. Например, если интерпретатор запущен из папки test/
и вы запускаете команду python ./packA/subA/subA1.py
, то sys.path
включает test/packA/subA/
, а не test/
.
Кроме того, значение sys.path
будет использоваться для всех импортированных модулей. Например, предположим, что мы вводим команду python start.py
. Пусть start.py
импортирует packA.a1
, и пусть a1.py
выведет содержимое переменной sys.path
. Выводимое содержимое переменной sys.path
будет включать test/
(путь к start.py
), а не test/packA/
(путь к a1.py
). Это означает, что скрипт a1.py
может вызывать import other
, поскольку файл other.py
находится в test/
.
Все о __init__.py
Файл __init__.py
выполняет 2 функции.
- Преобразование папки со скриптами в импортируемый пакет модулей (до Python 3.3).
- Запуск кода инициализации пакета.
Преобразование папки сценариев в импортируемый пакет модулей
Как уже было сказано выше, любой каталог, содержащий файл с именем __init__.py
представляет собой пакет Python. Этот файл так же может быть пустым. Например, при запуске скрипта start.py
в Python 2.7 можно импортировать пакет packA
, но не packB
, так как в каталоге test/packB/
нет файла __init__.py
.
Это НЕ применимо к Python 3.3 и выше, благодаря принятию неявных пространств имен пакетов. В принципе, Python 3.3+ рассматривает все папки как пакеты, поэтому пустые файлы __init__.py
больше не нужны и могут быть опущены.
Например, packB
представляет собой пространство имен пакета, поскольку в папке нет файла __init__.py
. Если мы запустим интерактивный интерпретатор Python версии 3.6 в каталоге test/
, то получим следующий результат:
>>> import packB >>> packB
Источники:
1. What is init.py for?
2. PEP 420: Implicit Namespace Packages.
Запуск кода инициализации пакета
В первый раз, когда вы импортируете пакет или один из его модулей, Python будет выполнять файл __init__.py
в корневой папке пакета, если этот файл существует. Все объекты и функции, определенные в __init__.py
, считаются частью пространства имен пакета.
Рассмотрим следующий пример.
Листинг файла test/packA/a1.py
:
def a1_func(): print("running a1_func()")
Листинг файла test/packA/__init__.py
:
## этот импорт делает a1_func доступной напрямую из packA.a1_func from packA.a1 import a1_func def packA_func(): print("running packA_func()")
Листинг файла test/start.py
:
import packA # "import packA.a1" будет работать также packA.packA_func() packA.a1_func() packA.a1.a1_func()
Команда python start.py
выведет следующее:
running packA_func() running a1_func() running a1_func()
Примечание: если в файле a1.py
вызывается import a2
, и вы запускаете в командной строке команду python a1.py
, то test/packA/__ init__.py
НЕ будет вызван, хотя на первый взгляд кажется, что a2
является частью пакета packA
. Так происходит потому, что Python запускает скрипт (в нашем случае a1.py
), но содержащая его папка не является пакетом.
Использование объектов из импортированного модуля или пакета
Существует четыре различных вида синтаксиса для записи операторов импорта.
import
import
from
import from
import
Пусть X
любое имя после ключевого слова import
.
- если
X
— это имя модуля или пакета, то для использования объектов, определенных вX
, вам нужно написатьX.object
. - если
X
является именем переменной, его можно использовать непосредственно напрямую. - если
X
является именем функции, то его можно вызвать с помощью инструкцииX()
.
Необязательно, но после любого оператора import X
может быть добавлена инструкция вида as Y
, например, import X as Y
. Эта инструкция переименовывает X
в Y
в пределах файла скрипта. Обратите внимание, что имя X
далее не действительно и его использовать не имеет смысла. Например import numpy as np
.
Аргументом при ключевом слове import
может быть одно имя или список из нескольких имен. Каждое из этих имен может быть также переименовано с помощью ключевого слова as
. Например, следующие инструкции импорта в файле в start.py
будут работать правильно: import packA as pA
, packA.a1, packA.subA.sa1 as sa1
.
Рассмотрим следующий пример: в файле start.py
необходимо импортировать функцию helloWorld()
из файла sa1.py
.
Решение №1:
from packA.subA.sa1 import helloWorld
затем мы можем вызывать функцию непосредственно по имени:
a = helloWorld()
Решение №2:
# следующие две строки кода эквивалентны from packA.subA import sa1 import packA.subA.sa1 as sa1
затем мы должны будем использовать в качестве префикса к имени функции имя модуля.
x = sa1.helloWorld()
Иногда это решение предпочтительнее Решения №1 для того чтобы сделать явным вызов функции helloWorld
из модуля sa1
.
Решение №3:
import packA.subA.sa1
Далее необходимо использовать полный путь:
x = packA.subA.sa1.helloWorld()
Использование dir() для проверки содержимого импортированного модуля
После импорта модуля, используйте функцию dir()
для того чтобы получить список доступных имен модуля. Например, предположим, что вы импортируете sa1
. Если в sa1.py
определена функция helloWorld()
, то инструкция dir(sa1)
в числе прочих выведет имя helloWorld
.
>>> from packA.subA import sa1 >>> dir(sa1) ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'helloWorld']
Импорт пакетов
Импорт пакета концептуально эквивалентен импорту файла __init__.py
из папки пакета в качестве модуля. И действительно это так. Вот как Python рассматривает импортируемый пакет:
>>> import packA >>> packA
Скриптам, импортирующим пакеты, доступны лишь те объекты, которые были объявлены в файле пакета __init__.py
. Например, так как каталог packB
не содержит файла __init__.py
, то вызов инструкции import packB
в Python 3.3+ будет мало полезен, поскольку никакие объекты при импорте пакета packB
не будут доступны. Последующий вызов packB.b1
завершится неудачей, поскольку объект не будет импортирован.
Импорт с использованием абсолютных и относительных путей
Импорт по абсолютному пути использует полный путь (начинающийся с корневой папки проекта) до требуемого для импорта модуля .
Импорт по относительному пути использует относительный путь (начинающийся с пути к текущему модулю) до требуемого для импорта модуля. Существует два вида импорта с использованием относительного пути:
- явный импорт задается в следующей форме
from .
, гдеimport X
имеет префикс в виде последовательности имен каталогов, разделенных точками.
, которые указывают, на сколько каталогов необходимо переместиться вверх или вниз. При этом одна точка.
соответствует текущему каталогу, а две точки..
предписывают переместиться на одну папку вверх и т.д. - неявный импорт записывается так, как будто текущий каталог является частью содержимого переменной
sys.path
. Неявный относительный импорт поддерживается только Python 2 и не поддерживается Python 3.
В документации Python говорится о том, как Python 3+ обрабатывает импорт по относительному пути:
Единственным верным синтаксисом для импорта по относительному пути является формат
from .[module] import name
. Все формы инструкции импорта не начинающиеся с точки.
интерпретируются как импорт по абсолютному пути.
Источник: What’s New in Python 3.0.
Например, предположим, что мы запускаем на исполнение скрипт start.py
, который импортирует a1
, который, в свою очередь, импортирует other
, a2
и sa1
. Инструкции import
в файле a1.py
будут выглядеть следующим образом:
# импорт по абсолютному пути import other import packA.a2 import packA.subA.sa1 # явный импорт по относительному пути import other from . import a2 from .subA import sa1 # неявный импорт по относительному пути (не поддерживается в Python 3) import other import a2 import subA.sa1
Импорт по относительному пути может выполняться только для модулей в пакете; не допускается использовать эту возможность для ссылки на модули, которые находятся в другом каталоге файловой системы. Обратите внимание, что при импорте по относительному пути, точки ..
помогут перемещаться при поиске файла для импорта только выше каталога (но не включая его), содержащего скрипт, запускаемый из командной строки. Таким образом, from .. import other
не будет работать в a1.py
. Это приводит к ошибке ValueError: attempted relative import beyond top-level package (попытка импорта по относительному пути за пределы пакета верхнего уровня).
В целом импорт по абсолютному пути предпочтительнее чем по относительному. Их использование позволяет избежать путаницы между явным или неявным импортом. Кроме того, любой сценарий, который использует явный импорт по относительному пути, не может быть запущен напрямую:
Обратите внимание, что импорт по относительному пути основан на имени текущего модуля. Поскольку имя основного модуля всегда «main», то модули, предназначенные для использования в качестве основного модуля приложения Python, должны всегда использовать импорт по абсолютному пути.
Источники: Python 2 и 3.
Источники:
1. How to accomplish relative import in python
2. Changes in import statement python3
Примеры
Пример 1: содержание sys.path заранее известно
Если вы запускаете скрипты командой python start.py
или python other.py
, то вам будет очень просто настроить импорт всех модулей. В этом случае переменная sys.path
будет всегда включать директорию test/
в списке путей поиска при импорте. Поэтому все операторы импорта могут быть записаны относительно папки test/
.
Например: сценарий в папке test
должен импортировать функцию helloWorld()
из файла sa1.py
.
Решение: from packA.subA.sa1 import helloWorld
Пример 2: содержание sys.path может изменяться
Часто мы хотим гибко использовать сценарии Python независимо от того выполняются ли они в командной строке или импортированы как модуль в другой скрипт. Как будет показано ниже, здесь мы столкнемся с проблемами, а в особенности в коде написанном на Python 3.
Пример: Предположим, что в сценарии start.py
необходимо импортировать a2.py
, который в свою очередь должен импортировать sa2.py
. Предположим, что start.py
всегда запускается непосредственно напрямую, и никогда не импортируется. Мы также хотим иметь возможность запускать a2.py
самостоятельно. Кажется, все достаточно просто. В конце концов, нам нужно всего лишь два оператора импорта: первый в start.py
и второй в a2.py
.
Проблема: Это случай, когда sys.path
будет изменяться. Когда мы запускаем start.py
, переменная sys.path
содержит директорию test/
. Когда мы запустим файл a2.py
, то sys.path
будет содержать test/ packA/
.
Инструкция import
в start.py
проста для понимания. Зная заранее, что start.py
будет всегда запускаться напрямую и никогда не будет импортирован, мы полагаем, что при его запуске путь test/
всегда будет находиться в переменной sys.path
. Тогда инструкция для импорта a2.py
будет следующая import packA.a2
.
Инструкция import
в a2.py
более сложна. Когда мы запускаем start.py
напрямую, переменная sys.path
содержит test/
, поэтому в a2.py
следует использовать инструкцию from packA.subA import sa2
. Однако, если после этого мы будем запускать a2.py
напрямую, то переменная sys.path
будет содержать путь test/packA/
. Теперь инструкция для импорта не будет корректной, поскольку packA
не является папкой внутри test/packA/
.
Вместо этого мы могли бы попробовать применить инструкцию from subA import sa2
. Это решит нашу проблему, если мы запускаем a2.py
напрямую. Но теперь у нас возникает проблема, когда мы напрямую запускаем start.py
. В Python 3 это не будет выполняться, потому что subA
не находится в переменной sys.path
. Но будет выполняться без ошибок в Python 2, благодаря поддержке неявного импорта с относительными путями.
Таким образом, по использованию инструкции import
в файле a2.py
имеем:
Инструкция | from packA.subA import sa2 | from subA import sa2 |
---|---|---|
start.py | Работает | в Py2 работает, в Py3 не работает (subA нет в test/) |
a2.py | Не работает (packA нет в test/packA/) | Работает |
Для полноты картины можно попытался использовать импорт по относительному пути: from .subA import sa2
. И это будет соответствовать результату выполнения инструкции from packA.subA import sa2
.
Решение (обходной путь решения проблемы): Мне ничего не известно о простом и наглядном способе решения этой проблемы. Вот некоторые обходные пути:
- Используйте импорт по абсолютному пути и
test/
как корневую директорию (средний столбец в таблице выше). Это гарантирует запуск скриптаstart.py
напрямую. Чтобы запуститьa2.py
напрямую, необходимо запустить его как импортированный модуль, а не как скрипт. Для этого надо изменить текущую директорию наtest/
в консоли, то есть выполнить командуpython -m packA.a2
. - Используйте импорт по абсолютному пути и
test/
как корневую директорию (средний столбец в таблице выше). Это гарантирует, что будет работать напрямую запускstart.py
. Чтобы напрямую запуститьa2.py
, мы можем изменить значение переменнойsys.path
вa2.py
, для того чтобы включить в список путей для импорта путьtest/packA/
, прежде чемsa2.py
будет импортирован.import os, sys sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) # now this works, even when a2.py is run directly from packA.subA import sa2
ПРИМЕЧАНИЕ. Обычно этот метод работает. Однако в некоторых установках Python переменная
__file__
может быть некорректной. В этом случае вам нужно будет использовать встроенный в стандартную библиотеку Python пакет inspect. Ознакомьтесь с вопросом на StackOverflow для правильного использования приведенных выше инструкций. - Используйте только Python 2 если используете неявный импорт по относительному пути (т.е. третий столбец в приведенной выше таблице).
- Используйте импорт по абсолютному пути, в корне директории
test/
, и добавьтеtest/
в переменную среды PYTHONPATH.- это решение не переносимо в другую среду выполнения, поэтому не рекомендуется его применять.
- инструкции по его использованию здесь: Как постоянно добавлять директорию в переменную среды PYTHONPATH
Пример 3: содержание sys.path может изменяться (вариант 2)
Более сложная проблема заключается в следующем. Предположим, что a2.py
никогда не нужно запускать напрямую, но он импортируется такими файлами, как start.py
и a1.py
, которые будут запускаться напрямую из консоли.
В этом случае Решение №1 не будет работать. Но другие будут.
Пример 4: Импорт из родительской директории
Если мы не собираемся модифицировать переменные PYTHONPATH и sys.path
в коде, то в этом случае основным ограничением импорта является следующее: При непосредственном запуске скрипта через консоль невозможно импортировать что-либо из его родительского каталога.
Например, если вы хотите запустить следующую команду python sa1.py
, то невозможно в файле sa1.py
что-либо импортировать из a1.py
, не прибегая к обходным путям, описанным выше: изменения значений переменных PYTHONPATH или sys.path
.
Во-первых, может показаться, что импорт по относительному пути (например, from .. import a1
) может обойти это ограничение. Однако скрипт, который выполняется (в данном случае sa1.py
) при этом считается «модулем верхнего уровня». Попытка импортировать что-либо из каталога на уровень выше каталога этого скрипта приводит к появлению следующей ошибки: ValueError: attempted relative import beyond top-level package (попытка импорта по относительному пути за пределы пакета верхнего уровня).
Можно не писать сценарии, в которых необходимо импортировать что-либо из родительского каталога. В тех случаях когда это все таки необходимо сделать предпочтительным обходным путем является изменение переменной sys.path
.
Python 2 VS. Python 3
Наиболее важные различия между тем, как Python 2 и Python 3 рассматривают инструкции import
, были описаны выше. Они снова рассматриваются ниже, наряду с некоторыми другими менее важными отличиями.
- Python 2 поддерживает неявный импорт по относительному пути, а Python 3 — нет.
- Python 2 требует, чтобы файлы
__init__.py
находились внутри папки, чтобы папка считалась пакетом и была импортирована. В отличие от него для Python 3.3 и выше, благодаря поддержке неявного пространства имен пакетов, все папки являются пакетами независимо от наличия в них файла__init__.py
. - В Python 2 можно написать
from
внутри функции. В Python 3 синтаксисimport from
разрешен только на уровне модуля, но не внутри функций.import
Источники:
1. Изменения в инструкциях import в Python 3
2. Модули в Python 2 (официальная документация)
3. Модули в Python 2 (официальная документация)
4. Что нового в Python 3