В своей работе программист обязан «разделять сущности» когда в коде нарастает сложность. Чаще всего это означает, что какой-то блок кода разбивают на более мелкие блоки, таки как функции, классы, методы класса… Но что делать, если самих этих блоков кода уже так много, что само их количество создаёт сложность? Приходится «повышать уровень абстракции» — разделять код на несколько файлов. В этом уроке мы научимся использовать несколько таких файлов как целостный код. Одной из важных характеристик языка программирования является то, как в нём устроен этот механизм. Что ж, Python как обычно предоставляет удобный механизм, представляющий собой систему импортов файлов.
Давайте договоримся о терминах: любой файл с кодом на Питоне может использоваться в другом файле Питона и называется в таком случае модулем или библиотекой. Кстати, Python так же может использовать файлы на C или C++.
Давайте разбираться!
Подключение модуля из стандартной библиотеки
Стандартная библиотека – это набор наиболее распространённых и используемых модулей, которые поставляются вместе с языком Пайтон. Другими словами, когда Вы устанавливаете язык на свой компьютер, этот набор идёт в комплекте.
Для того, чтоб получить доступ к какому-то модулю из стандартной библиотеки достаточно написать в коде ключевое слово import Python, вот так:
import sys
Эта строка импортирует из стандартной библиотеки модуль sys.
print(sys)
print(type(sys))
# Вывод:
<module 'sys' (built-in)>
<class 'module'>
На самом деле, при импорте c Python import создаётся переменная sys, которая хранит ссылку на файл:
import sys
print('sys', sys)
print('type(sys)', type(sys))
os = sys
sys = 1
print('sys', sys)
print('type(sys)', type(sys))
print('os', os)
print('type(os)', type(os))
# Вывод:
sys <module 'sys' (built-in)>
type(sys) <class 'module'>
sys 1
type(sys) <class 'int'>
os <module 'sys' (built-in)>
type(os) <class 'module'>
Модуль sys помогает взаимодействовать с системой Вашего компьютера. Теперь можно использовать объекты и функции из этой библиотеки в своём коде. Делается это при помощи синтаксиса: «модуль.атрибут». К примеру, узнать, какой тип операционной системы используется:
import sys
print(sys.platform)
# Вывод:
win32
После ключевого слова import можно указать не одну, а несколько Python modules (библиотек). Однако, так делать не стоит, поскольку от этого ухудшается читаемость кода:
import sys, pprint
pprint.pprint(sys.platform)
# Вывод:
'win32'
Важно знать, что при import module код из модуля сразу же исполняется. К примеру, если импортировать следующий код:
def foo():
print("foo")
print("bar")
то в терминале появится:
bar
Надпись foo не появилась, так как функция пока нигде не вызывалась. Если сделать так:
def foo():
print("foo")
print("bar")
foo()
то в терминал будет выведено:
bar
foo
То, что код начинает выполняться сразу, сделано для того, чтоб можно было произвести инициализацию модуля, то есть подготовить его каким-то образом перед использованием. К примеру, запустить сервер или заполнить переменные значениями. Если у Вас есть код, который должен выполняться, когда Вы запускаете скрипт, но не должен, когда Вы импортируете этот скрипт, используйте стандартный приём:
def foo():
print("foo")
if __name__ == '__main__':
print("bar")
foo()
Теперь код будет исполняться так, как описано выше.
Ошибки
Если попытаться использовать что-то, чего нет в модуле, интерпретатор вернёт исключение AttributeError:
import sys
print(sys.wrong_argument)
# Вывод:
Traceback (most recent call last):
File "C:\Users\ivand\AppData\Roaming\JetBrains\PyCharm2021.2\scratches\scratch.py", line 4, in <module>
print(sys.wrong_argument)
AttributeError: module 'sys' has no attribute 'wrong_argument'
Process finished with exit code 1
Если попытаться импортировать не существующий модуль, Вы увидите исключение ModuleNotFoundError:
import wrong_module
# Вывод:
Traceback (most recent call last):
File "C:\Users\ivand\AppData\Roaming\JetBrains\PyCharm2021.2\scratches\scratch.py", line 1, in <module>
import wrong_module
ModuleNotFoundError: No module named 'wrong_module'
Process finished with exit code 1
Использование псевдонимов
Распространённой практикой является использование коротких псевдонимов для длинных названий импортируемого модуля. Для этого существует специальное слово as.
import itertools as iter
print(iter)
# Вывод:
<module 'itertools' (built-in)>
На самом деле as – лишь «синтаксический сахар», ведь происходит здесь то же самое, что мы проделали чуть раньше: замена переменной, хранящей ссылку на модуль.
import itertools as iter
import itertools
it = itertools
print(it is iter)
# Вывод:
True
Инструкция from
Зачастую не требуется весь модуль, а лишь какая-то его часть. Вы ведь не покупаете чайную лавку целиком, когда вам нужна одна упаковка чая?
В Python есть ключевое слово from, которое позволяет импортировать только нужные части. Простейший случай использования выглядит так:
from sys import *
Это импортирует все сущности из модуля и теперь к ним можно обращаться так, будто они объявлены в текущем файле:
from sys import *
print(platform)
# Вывод:
win32
Но такой вариант является максимально плохим! Почему? Всё дело в пространстве имён. Если пользоваться этим способом, пространство имён выходит из-под Вашего контроля – теперь Вы не знаете какие имена переменных, функций, классов используются в Вашей программе, из-за чего могут возникнуть неприятности. В конце концов, Вы же не собираетесь, не глядя купить всё, что есть в чайной лавке?
Для того, чтоб импортировать только определённые сущности, укажите их имена после ключевого слова import (можно несколько).
from sys import platform
print(platform)
# Вывод:
win32
И здесь тоже можно использовать псевдонимы.
from sys import platform as pl
print(pl)
# Вывод:
win32
Создание своего модуля на Python
Как уже говорилось выше, любой код на Python является модулем, так что создавать модули очень просто. К примеру, создадим файл hello.py:
def say():
print("Hello!")
Теперь в той же директории создадим файл talk.py, в который будем импортировать функцию say:
from hello import say
say()
# Вывод:
Hello!
Да, всё так просто!
А как установить модуль, написанный кем-то другим? Используйте pip instal.
Именование модулей
Здесь есть ограничения. Самое важное – нельзя именовать модуль ключевым словом самого Пайтона (к примеру, pass или for), иначе его просто не получится импортировать. Так же не стоит использовать идентификаторы Питона (к примеру, class или type). Получить полный список зарезервированных слов и идентификаторов можно при помощи следующего кода:
print(help("keywords"))
# Вывод:
Here is a list of the Python keywords. Enter any keyword to get more help.
False break for not
None class from or
True continue global pass
__peg_parser__ def if raise
and del import return
as elif in try
assert else is while
async except lambda with
await finally nonlocal yield
None
Кроме того, не желательно давать модулю такое же имя, как у модулей встроенной библиотеки, либо какого-либо из распространённых сторонних модулей – это вносит не нужную неоднозначность.
Куда поместить модуль?
Туда, где его потом можно будет найти. Пути поиска модулей указаны в переменной sys.path. В него включены текущая директория (то есть модуль можно оставить в папке с основной программой), а также директории, в которых установлен python. Кроме того, переменную sys.path можно изменять вручную, что позволяет положить модуль в любое удобное для вас место (главное, не забыть в главной программе модифицировать sys.path).
Пакеты
Что же делать, если Ваш проект разрастается и модулей уже очень много? Пайтон предлагает элегантное решение: почему бы не использовать уже существующую в операционной системе иерархию – директории (папки)? В Питоне это называется пакеты.
Пакет в Python — директория с обязательным модулем __init__.py. Остальное содержимое может включать в себя и модули, и другие пакеты.
__init__.py – файл, который выполняется сразу после импорта пакета и предназначен для инициализации – предварительной подготовки перед использованием содержимого пакета. Этот механизм аналогичен тому, как выполняется основной поток кода при импорте модуля.
Если пакет не содержит __init__.py, то он превращается в так называемое «пространство имён» — может содержать другие пакеты, но не может содержать модули на первом уровне.