Codebra
29 января 2026 в 18:34

Урок 60. Еще о возможностях модулей в Python

В этом уроке закроем все оставшиеся темы, связанные с модулями и пакетами в Python. Переменные __future__, __name__, аргументы командной строки и изменение пути поиска модулей.
📝

Внимание! На этой странице вы найдете материал урока из архивного курса по Python. Курс был написан в 2024 году и по-прежнему актуален для начинающих разработчиков.

Теоретический материал сохранен в исходном виде, а практические задания с автоматической проверкой вынесены в отдельные интенсивы и задания.

Полный список уроков доступен по тегу Архивный курс по Python и на странице первого урока.

📝 Кратко
  • В Python любой код всегда находится внутри модуля.
  • Сведите к минимуму количество глобальных переменных.
  • Модули должны в редких случаях менять переменные других модулей.
  • Лучше обмениваться данными между модулями при помощи аргументов функций и возвращаемых значений.
  • Сокрытие данных в модулях: использование нижнего подчеркивания для приватных переменных.
  • Включение будущих средств языка: оператор import с необязательными расширениями.
  • Переменная __name__ в Python: используется для определения, является ли файл модулем или сценарием.
  • Аргументы командной строки в Python: модуль sys для работы с ними.
  • Изменение пути поиска модулей в Python: использование функции exec() для динамического выполнения кода.

Введение

В предыдущем уроке мы познакомились с пакетами в Python, еще ранее научились писать модули. Осталось поговорить об оставшихся темах, которые не вошли в предыдущие уроки: переменная __name__, модуль __future__, сокрытие данных и другие.

Для начала рассмотрим основные принципы и идеи, связанные с модулями.

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

Сведите к минимуму количество глобальный переменных. Про инкапсуляцию поговорим в теме по ООП.

Модули должны в редких случаях менять переменные других модулей. Использование глобальных переменных является частой практикой для передачи результатов между модулями. Зачастую такое решение следует из-за неправильного проектирования (за исключением некоторых случаев) и приводит к запутыванию кода и усложнению его дальнейшей поддержки. Лучше обмениваться данными между модулями при помощи аргументов функций и возвращаемых значений.

Сокрытие данных в модулях

Иногда нам необходимо ограничить доступ к некоторым данным извне модуля. В этом уроке «сокрытие данных» и «инкапсуляция» будем считать синонимами. Сразу приведу пример, а дальше разберемся:

''' b.py '''  
a = 1  
_b = 2

И основной файл:

'''  a.py  '''
from b import *

print(a)
print(_b)

В Python все имена модуля являются публичными, т.е. доступными за пределами модуля. Но, если имя начинается с нижнего подчеркивания (как переменная _b), то оно становится приватным, т.е. недоступным за пределами модуля. Попробуйте запустить вышеуказанный код и убедиться в этом.

Такой способ сделать имя приватным работает только с оператором from … *. Давайте перепишем пример с использованием обычного импортирования при помощи import.

'''  a.py  '''
import b

print(b.a)
print(b._b)

Как видите, переменная _b не такая уже и приватная на самом деле. Взглянем на другой (противоположный) способ: переменная __all__. Python сначала ищет в модуле переменную __all__, содержащую список с именами (независимо, есть ли перед ними подчеркивание) и делает их все публичными, т.е. оператор from … * копирует их в файл верхнего уровня, а все остальные имена становятся «приватными».

'''  b.py  '''
__all__= ['_b']
a = 1
_b = 2

Файл верхнего уровня:

'''  a.py  '''
from b import *

print(a)
print(_b)

При использовании обычного импортирования при помощи оператора import, мнимая приватность не работает.

Включение будущих средств языка: __future__

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

from __future__ import название

Переменная __name__ в Python

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

'''  a.py  '''

def print_x(x):
    print(x)

def main():
    print('Выполнение сценария началось')
    print_x(10)

if __name__ == '__main__':
    main()

Если вы программировали на С/С++, то «упаковка» кода внутрь функции main() вас не удивит. Запустите этот код и убедитесь в его очевидной работе. Он работает точно так же, как и этот:

'''  a.py  '''

def print_x(x):
    print(x)

print('Выполнение сценария началось')
print_x(10)

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

'''  b.py  '''
import a

a.print_x(12)

Попробуйте запустить этот скрипт с двумя вариантами файла a.py по очереди. Когда файл a.py используется как модуль, переменная __name__ устанавливается в false и функция main() не выполняется. Как вы догадались, первый пример файла a.py можно было записать следующим образом:

'''  a.py  '''

def print_x(x):
    print(x)

if __name__ == '__main__':
    print('Выполнение сценария началось')
    print_x(10)

Если вы забыли, как работают функции, то можете повторить раздел «Функции», начиная с урока «Урок 48. Основы функций в Python».

Аргументы командной строки

Если вы знакомы с С/С++, то вероятно знаете про аргументы командной строки:

int main(int argc, char* argv[]) { /* ... */ }

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

'''  a.py  '''
import sys

if __name__ == '__main__':
    print(sys.argv)

Во-первых, следует подключить модуль sys. Этот модуль имеет список аргументов argv, в котором хранится первым элементом путь до запускаемого скрипта, а далее переданные через командную строку аргументы:

a.py 123 Слово

Скрипт a.py напечатает в консоли:

['a.py', '123', 'Слово']

Как перебирать списки вы научились в уроке «Урок 18. Списки в Python».

Изменение пути поиска модулей

В первом уроке по модулям «Урок 57. Модули в Python» мы говорили о возможности изменения путей поиска, но не показывали, как это сделать. Напомню, по итогу образуется список из путей, который хранится под именем path в модуле sys. Чтобы посмотреть список путей, в которых осуществляется поиск, можно просто вывести этот список:

'''  a.py  '''
import sys
print(sys.path)

Мы уже несколько раз сказали: «Это список». Этот список мы можем редактировать как нам вздумается: добавлять элементы, удалять их или полностью очистить список. О методах работы со списками мы говорили в уроке «Урок 18. Списки в Python». Например, добавим путь:

sys.path.append('c:\code')

Выполнение строк с кодом

Если вдруг вам необходимо импортировать модуль, название которого «создается» динамически, можете попробовать так:

import 'module_1'

И получить ошибку о неправильном синтаксисе. Хорошо. Попробуем обмануть и укажем динамически созданное название модуля в переменной:

x =  'module_1'
import x

Синтаксической ошибки не будет, но и модуль module_1 не будет подключен, так как интерпретатор будет искать с именем x. Переменная будет восприниматься как название модуля. Не расстраивайтесь, мы можем это обойти при помощи функции exec(), которая динамически выполняет большие блоки Python-кода.

x =  'module_1'
exec('import ' + x)

Разумеется, это не единственное применение функции exec(). Функция exec() и похожая на нее eval() (выполняет строку кода) таят в себе множество опасностей, особенно, если в эти функции передаются данные из недостоверного источника. В интернете полно статей на эту тему и еще больше споров о возможности сделать эти функции безопасными для применения.

Что касается предварительной компиляции динамически созданного модуля, мы можем использовать вместо exec() встроенную функцию __import__, возвращающую объект модуля.

x =  'module_1'
__import__(x)

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

📝

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