Если вы работали над проектом Python, в котором есть более одного файла, скорее всего, вам приходилось использовать оператор импорта раньше.

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

В этом руководстве вы узнаете о различиях между ними, а также об их плюсах и минусах. Давайте прямо сейчас!

Содержание

Краткий обзор импорта    ↑

Вам необходимо хорошо разбираться в модулях и пакетах Python, чтобы знать, как работает импорт. Модуль Python — это файл с расширением .py, а пакет Python — это любая папка, в которой есть модули (или, в Python 2, папка, содержащая файл __init__.py). Что произойдет, если у вас есть код в одном модуле, которому требуется доступ к коду в другом модуле или пакете? Вы его импортируете!

Как работает импорт    ↑

Но как именно работает импорт? Допустим, вы импортируете модуль abc следующим образом:

import abc

Первым делом Python найдет имя abc в sys.modules. Это кеш всех ранее импортированных модулей.

Если имя не найдено в кэше модуля, Python продолжит поиск по списку встроенных модулей. Это модули, которые поставляются заранее, устанавливается вместе с Python и находится в стандартной библиотеке Python. Если имя по-прежнему не найдено во встроенных модулях, Python ищет его в списке каталогов, определенных sys.path. Этот список обычно включает текущий каталог, поиск по которому выполняется в первую очередь.

Когда Python находит модуль, он связывает его с именем в локальной области видимости. Это означает, что abc теперь определен и может использоваться в текущем файле без выдачи NameError.

Если имя не будет найдено, вы получите ошибку ModuleNotFoundError. Вы можете узнать больше об импорте в документации Python здесь!

Примечание: проблемы безопасности

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

Вот несколько интересных ресурсов, чтобы узнать больше об этих проблемах безопасности и способах их устранения:

Синтаксис операторов импорта    ↑

Теперь, когда вы знаете, как работают операторы импорта, давайте рассмотрим их синтаксис. Вы можете импортировать как пакеты, так и модули. (Обратите внимание, что при импорте пакета, по сути, импортируется файл __init__.py пакета как модуль). Вы также можете импортировать определенные объекты из пакета или модуля.

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

import abc

abc может быть пакетом или модулем.

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

from abc import xyz

xyz может быть модулем, подпакетом или объектом, например классом или функцией.

Вы также можете переименовать импортированный ресурс, например:

import abc as other_name

Это переименовывает импортированный ресурс abc в other_name в сценарии. Теперь на него нужно ссылаться как на other_name, иначе он не будет распознан.

Стиль операторов импорта    ↑

PEP 8, официальное руководство по стилю для Python, дает несколько советов по написанию операторов импорта. Вот краткое изложение:

  1. Импорт всегда должен быть написан в верхней части файла, после любых комментариев модуля и строк документации.
  2. Импорт следует разделить в зависимости от того, что импортируется. Обычно есть три группы:
    • импорт стандартной библиотеки (встроенные модули Python)
    • связанный сторонний импорт (модули, которые установлены и не относятся к текущему приложению)
    • импорт локального приложения (модули, принадлежащие текущему приложению)
  3. Каждая группа импорта должна быть разделена пробелом.

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

Вот пример того, как стилизовать операторы импорта:

"""Иллюстрация хорошего стиля оператора импорта.

Обратите внимание, что импорт идет после строки документации.

"""

# Импорт стандартной библиотеки
import datetime
import os

# Сторонний импорт
from flask import Flask
from flask_restful import Api
from flask_sqlalchemy import SQLAlchemy

# Импорт локальных приложений
from local_module import local_class
from local_package import local_function

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

Абсолютный импорт    ↑

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

Абсолютный импорт указывает ресурс, который будет импортирован, с использованием его полного пути из корневой папки проекта.

Синтаксис и практические примеры    ↑

Допустим, у вас есть следующая структура каталогов:

└── project
    ├── package1
    │   ├── module1.py
    │   └── module2.py
    └── package2
        ├── __init__.py
        ├── module3.py
        ├── module4.py
        └── subpackage1
            └── module5.py

Есть каталог project, который содержит два подкаталога, package1 и package2. В каталоге package1 есть два файла: module1.py и module2.py.

В каталоге package2 есть три файла: два модуля, module3.py и module4.py, и файл инициализации __init__.py. Он также содержит каталог, подпакет, где, в свою очередь, содержится файл, module5.py.

Предположим следующее:

  1. package1/module2.py содержит функцию function1.
  2. package2/__ init__.py содержит класс class1.
  3. package2/subpackage1/ module5.py содержит функцию function2.

Ниже приведены практические примеры абсолютного импорта:

from package1 import module1
from package1.module2 import function1
from package2 import class1
from package2.subpackage1.module5 import function2

Обратите внимание, что вы должны указать подробный путь для каждого пакета или файла из папки пакета верхнего уровня. Это несколько похоже на путь к файлу, но мы используем точку (.) Вместо косой черты (/).

Плюсы и минусы абсолютного импорта    ↑

Абсолютный импорт предпочтительнее, потому что он достаточно ясен и прост. Легко определить, где именно находится импортированный ресурс, просто взглянув на инструкцию. Кроме того, абсолютный импорт остается действительным, даже если текущее расположение оператора импорта изменяется. Фактически, PEP 8 явно рекомендует абсолютный импорт.

Иногда, однако абсолютный импорт может быть довольно подробным, в зависимости от сложности структуры каталогов. Представьте себе такое утверждение:

from package1.subpackage2.subpackage3.subpackage4.module5 import function6

Ведь смешно? К счастью, в таких случаях хорошей альтернативой является относительный импорт!

Относительный импорт    ↑

Относительный импорт указывает ресурс, который нужно импортировать, относительно текущего местоположения, то есть местоположения, где находится оператор импорта. Есть два типа относительного импорта: неявный и явный. Неявный относительный импорт объявлен устаревшим в Python 3, поэтому я не буду здесь на них останавливаться.

Синтаксис и практические примеры    ↑

Синтаксис относительного импорта зависит от текущего местоположения, а также от местоположения модуля, пакета или объекта, который нужно импортировать. Вот несколько примеров относительного импорта:

from .some_module import some_class
from ..some_package import some_function
from . import some_class

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

Одиночная точка означает, что указанный модуль или пакет находится в том же каталоге, что и текущее расположение. Две точки означают, что он находится в родительском каталоге текущего местоположения, то есть в каталоге выше. Три точки означают, что он находится в каталоге дедушки и бабушки и так далее. Это, вероятно, вам уже знакомо, если вы используете Unix-подобную операционную систему!

Предположим, у вас та же структура каталогов, что и раньше:

└── project
    ├── package1
    │   ├── module1.py
    │   └── module2.py
    └── package2
        ├── __init__.py
        ├── module3.py
        ├── module4.py
        └── subpackage1
            └── module5.py

Напомним содержимое файла:

  1. package1/module2.py содержит функцию function1.
  2. package2/__ init__.py содержит класс class1.
  3. package2/subpackage1/module5.py содержит функцию function2.

Вы можете импортировать function1 в файл package1/module1.py следующим образом:

# package1/module1.py

from .module2 import function1

Вы бы использовали здесь только одну точку, потому что module2.py находится в том же каталоге, что и текущий модуль, то есть module1.py.

Вы можете импортировать class1 и function2 в файл package2/module3.py следующим образом:

# package2/module3.py

from . import class1
from .subpackage1.module5 import function2

В первом операторе импорта единственная точка означает, что вы импортируете class1 из текущего пакета. Помните, что импорт пакета по сути импортирует файл __init__.py пакета как модуль.

Во втором операторе импорта вы снова должны использовать одну точку, потому что subpackage1 находится в том же каталоге, что и текущий модуль, которым является module3.py.

Плюсы и минусы относительного импорта    ↑

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

from ..subpackage4.module5 import function6

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

Заключение    ↑

Хорошая работа, если дошли до конца этого ускоренного курса по абсолютному и относительному импорту! Теперь вы знаете, как работает импорт. Вы ознакомились с передовыми методами написания операторов импорта и знаете разницу между абсолютным и относительным импортом.

С вашими новыми навыками, можно уверенно импортировать пакеты и модули из стандартной библиотеки Python, сторонних пакетов и ваших собственных локальных пакетов. Помните, что вам обычно следует отдавать предпочтение абсолютному импорту, а не относительному, если только путь не сложный и не сделает оператор слишком длинным.

Спасибо за чтение!

По мотивам Absolute vs Relative Imports in Python

Опубликовано Вадим В. Костерин

ст. преп. кафедры ЦЭиИТ. Автор более 130 научных и учебно-методических работ. Лауреат ВДНХ (серебряная медаль).

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *