Все статьи по Python
22 февр. 2025 г. - 20 мин. чтения
Модули и пакеты

Модули и пакеты

Работа с модулями. Объединение отдельных модулей в пакет. Пример использования модуля random

@ashtana

Штана Альберт Игоревич

Модули и пакеты

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

В Python любой файл с кодом называется модулем — между этими терминами нет разницы. Чтобы всем Python-разработчикам было удобно читать готовый код, принято называть файлы в стиле snake_case: то есть с маленькой буквы и с разделением слов символом подчеркивания. Это легко запомнить: snake_case переводится как «змеиный регистр» и поэтому идеально сочетается с языком Python.

Как использовать модули

Работать с кодом в тысячи строчек намного проще, если он разбит на несколько модулей. В таком случае обычно работают с главным файлом, а отдельные функции помещают в разные модули. Затем модули импортируют в main.py с помощью ключевого слова import. В итоге команда разработчиков получает код, который легко читать и обслуживать. Чтобы попрактиковаться, попробуем создать свой модуль. Для этого мы создадим файл с названием greeting.py. Затем внутри этого файла определим функцию say_hi() и переменную name:

# file: greeting.py
def say_hi(): # определяем функцию
    print('Hi!')

name = 'Alexander' # определяем переменную

Модуль-приветствие готов: он умеет печатать строку Hi! и обрабатывать переменную name. Но от него нет никакой пользы, пока он не встроен в работу всей остальной программы. Чтобы воспользоваться нашим модулем, нужно импортировать его в главный модуль main.py. Для этого в Python есть три способа:

  • Импорт модуля целиком;
  • Импорт отдельных определений из модуля;
  • Импорт всего содержимого модуля сразу;

Сначала рассмотрим только первый и второй способы.

Импорт модуля целиком

Это самый простой способ: используем ключевое слово import вместе с названием файла без расширения .py. Перейдем в главный файл main.py и импортируем туда наш модуль greeting.py:

# file: main.py
import greeting

Импорт прошел успешно — у нас появился доступ к модулю-приветствию прямо из главного файла. Теперь к содержимому модуля можно обращаться «через точку». Так можно вызвать функцию модуля или отдельную переменную:

# вызываем функцию модуля
greeting.say_hi()  # => Hi!

# выводим на экран отдельную переменную
print(greeting.name)  # => Alexander

Это самый распространенный способ, потому что такой код легко читать. Любой разработчик с первого взгляда может определить, что используемая переменная или вызываемая функция — это часть какого-то конкретного модуля.

Импорт отдельных определений

Иногда первый способ не подходит — например, из длинного и сложного модуля вам нужна всего пара функций или переменных. Тогда поможет второй способ импорта: нужно написать ключевое слово from с названием модуля без расширения .py. Затем в той же строчке указываем ключевое слово import с названиями определений, которые хотим использовать. Так это выглядит в коде:

# file: main.py
from greeting import say_hi, name # импортируем отдельные компоненты модуля

print(name)  # используем импортированную переменную
say_hi()     # вызываем импортированную функцию

Также при импорте можно указать новое имя для импортируемого компонента с помощью ключевого слова as:

# file: main.py
from greeting import say_hi as hello, name as first_name

print(first_name)
hello()

Во время импорта всего модуля можно обращаться к нему «через точку». Вот как выглядит такая запись: module.name. Для такой формы записи существует и более официальный термин — квалифицированное имя. Соответственно, импорт модуля целиком официально называется квалифицированным импортом. Стоит отметить, что в Python все строчки с import принято располагать в самом начале кода модуля. Такой набор строчек часто называют блоком импортов, хотя синтаксически этот блок никак не выделен — это обычные строчки одна за другой. Эта группировка повышает читаемость кода: так разработчикам проще искать ошибки в коде и исправлять их.

Импорт всего содержимого модуля

Рассмотрим третий вариант — импорт всего содержимого модуля. Так он выглядит в коде:

from some_module import *
from another_module import *

Здесь из модулей some_module и another_module неявно импортируются все определения. Такой способ импорта дает доступ к десяткам переменных, констант и функций, причем можно не указывать названия содержимого из всего модуля. Эта особенность отличает импорт содержимого от импорта модуля целиком — и она же может усложнять чтение кода. Посмотрим на примере. Представим программиста, который читает чужой код с импортом содержимого. В какой-то момент он встречает название незнакомой переменной и хочет узнать, откуда она взялась. Недостаточно просто внимательно посмотреть на блок импортов или сделать поиск по коду — названия всех импортированных определений скрываются за *. Именно поэтому в большинстве руководств по написанию кода на Python повторяется один и тот же совет: «Как можно реже пользуйтесь импортом всего содержимого». Тем не менее в реальном коде такие импорты встречаются, поэтому было важно упомянуть этот вариант.

Сочетание способов импорта

Чтобы углубить знания, рассмотрим еще один способ — сочетание импорта целиком и отдельными определениями в рамках одного и того же модуля. Рассмотрим пример. В модуле computation.py определим функцию и переменные:

# file: computation.py
PI = 3.1415926
E = 2.7182818


def pi_times(x):
    return x * PI

А в модуле main.py воспользуемся двумя способами импорта из модуля computation.py:

# file: main.py
import computation
from computation import PI, E
from computation import pi_times


print(PI)
print(computation.E)
print(pi_times(2))
print(computation.pi_times(E))

Из примеров выше видно, что можно:

  • Использовать оба способа импорта одновременно;
  • Импортировать отдельные определения в несколько заходов;
  • Получать доступ «через точку» к определениям, которые уже импортированы по имени;

Пакеты

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

Создание пакета

С точки зрения структуры пакет — это каталог (директория) с файлами модулей, имеющий имя в формате snake_case и содержащий специальный модуль с именем «__init__.py». Именно наличие этого специального файла подсказывает интерпретатору Python, что каталог следует воспринимать как пакет. Рассмотрим пример самого простого пакета. Создадим пакет из каталога package и модуля __init__.py внутри этого каталога:

package/
└── __init__.py

В файл __init__.py добавим следующий код:

# file __init__.py
NAME = 'super_package'

Теперь у нас есть небольшой, но уже полноценный пакет. Его можно импортировать так же, как модуль:

import package

print(package.NAME)

Обратите внимание, что отдельно импортировать файл __init__.py не нужно. При первом обращении к пакету Python самостоятельно импортирует модуль __init__.py. Это происходит автоматически, потому что каталог без init-файла не будет считаться пакетом.

Добавление модуля в пакет

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

package/
├── constants.py
├── functions.py
└── __init__.py

Чтобы было понятнее, рассмотрим содержимое модуля constants.py:

# file constants.py
PERSON = 'Alex'

И содержимое модуля functions.py:

# file functions.py
def greet(who):
    print('Hello, ' + who + '!')

Теперь в пакете есть не только __init__.py, но и еще два модуля — теперь их можно импортировать.

Как импортировать пакеты

Про модули упоминались два распространенных варианта импорта:

  • Квалифицированный импорт (также его называют «импорт модуля целиком»);
  • Импорт отдельных определений.

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

import package.functions
import package.constants

package.functions.greet(package.constants.PERSON)  # => Hello, Alex!

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

from package.functions import greet
from package.constants import PERSON

greet(PERSON)  # => Hello, Alex!

Кроме того, импорты в Python бывают двух видов:

  • Абсолютные;
  • Относительные.

Для понимания пакетов критически важно обсудить подробности этих двух видов импортов.

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

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

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

Относительные импорты выглядят так:

from . import module
from .module import function
from .subpackage.module import CONSTANT

В относительном импорте используется точка, которая означает импорт модуля из текущей директории.

Например, мы работаем с файлом main.py и хотим импортировать .module. При этом мы знаем, что main.py и .module хранятся в одной и той же директории. Тогда можно не прописывать абсолютный путь к .module, а воспользоваться . import. По этой точке Python автоматически определит, что и откуда мы хотим импортировать.

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

Модуль random

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

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

  • randint — сгенерировать целое число в заданном диапазоне;
  • choice — выбрать случайный элемент из заданного набора.

Генерация случайных чисел

Чтобы сгенерировать случайное число, нужно импортировать функцию randint из модуля random:

from random import randint

Для примера попробуем сгенерировать число от 1 до 100:

random_number = randint(1, 100)

Обратите внимание, что обе границы диапазона включены — значит, randint может выдать любое значение в диапазоне, в том числе 1 и 100. Перейдем к более сложному примеру:

string = 'abcde'
random_index = randint(0, len(string) - 1)
char = string[random_index]

Здесь программа должна выбрать случайный символ из строки string. При этом:

  • Строка в переменной string имеет длину 5;
  • Индекс последнего элемента в строке равен 4;
  • Символы строки индексируются с нуля.

Если мы попробуем сгенерировать число через randint(0, 5), то в какой-то момент получим значение 5. Тогда программа выдаст ошибку IndexError: она не сможет выдать пятый символ из четырех. Как это предотвратить? Нужно не просто задать верхнюю границу диапазона, а вычислить ее — то есть вычесть единицу из длины строки. Именно так и сделано в примере кода выше.

Выбор случайного элемента

Выше мы рассмотрели пример, в котором выбирается случайный символ строки. Эта задача возникает достаточно часто, поэтому в модуле random существует функция choice. Если использовать эту функцию, выбор случайного символа из строки будет выглядеть так:

from random import choice

string = 'abcde'
char = choice(string)

Используя choice, не нужно думать о границах диапазона — функция сама определяет, как правильно выбирать элементы. При этом важно, чтобы строка для выбора не была пустой. Иначе мы получим ошибку IndexError: Cannot choose from an empty sequence т.е. «Нельзя выбрать из пустой строки».

В основе модуля random лежит генератор псевдослучайных чисел — на самом деле, число выбирается не случайно, а на основе сложных математических вычислений. Из-за этого random не принято использовать в сферах, где нужна повышенная безопасность. В криптографии, шифровании и других подобных сферах используют модуль secrets, в основе которого менее предсказуемый механизм случайной генерации. Подробнее о модуле random почитайте по ссылке: Документация по модулю random.

Статья про генерацию случайных чисел.

Попробуйте сами запустить код в окне ниже с интерпретатором Python и повторите примеры из статьи чтобы самим увидеть и понять как всё это работает. Для этого в ячейке с кодом нажмите клавиши на клавиатуре Shift+Enter или запустите код через кнопку Run по значку ▶.