Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Запускаемые модули и пакеты Python: Настройка окружения

Как писать скрипты, мы уже узнали. Теперь представим, что у нас есть файл с кодом, который мы запускаем как скрипт. Файл разрастается, в нём появляются функции и прочие определения. В какой-то момент мы понимаем, что хотим переиспользовать, скажем, функцию из этого модуля в другом модуле. Значит, нужно импортировать!

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

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

# file <first_script.py>

def greet(who):
    print('Hello, {}!'.format(who))

greet('Bob')
greet('Ann')

А так — новый скрипт, в котором мы хотим переиспользовать функцию greet из первого модуля (скрипты — тоже модули):

# file <second_script.py>

from first_script import greet

greet('Thomas')

Запустим первый скрипт, а затем — второй (оба файла расположены в текущей директории):

$ python3 first_script.py
Hello, Bob!
Hello, Ann!
$ python3 second_script.py
Hello, Bob!
Hello, Ann!
Hello, Thomas!

Что мы видим — при выполнении второго скрипта выполнился и первый, хотя мы всего лишь импортировали из него одну функцию! Такова цена за простоту написания скриптов! Поскольку файл первого скрипта содержит не только определения, но и непосредственные действия (statements), то при загрузке файла (которая происходит при импорте модуля) эти действия будут выполнены. Теперь представьте, что мы бы импортировали скрипт, в котором не просто что-то печатается на экран, а удаляются какие-то файлы или того хуже — запускаются межконтинентальные ракеты!

Выходит, нам нужно как-то различать ситуации когда

  1. модуль выполняется как скрипт (выполняем побочные действия),
  2. модуль или его содержимое импортируются (не выполняем побочные действия).

Для этого нам понадобится немного магии!

Специальная переменная __name__

Механизм импорта при загрузке модуля в первый раз (первый для текущего запуска интерпретатора) добавляет в этот модуль несколько переменных специального вида. Этих переменных довольно много, но нам пока интересна одна — переменная __name__.

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

Что же хранит переменная __name__ в каждом конкретном случае? В этом и весь секрет!

  • Если происходит обычный импорт, то эта переменная содержит полное имя модуля (полностью квалифицированное).
  • Если же происходит запуск в качестве скрипта, то переменная получает специальное значение — строку '__main__'.

Глядя на значение этой переменной, можем отличать "запуск" от импортирования.

Слово "main" используется во многих языках для именования функции, которая вызывается автоматически при старте программы, потому и в Python это слово используется в похожем смысле

Давайте вернёмся к нашему примеру и перепишем first_script.py с применением этого нового знания:

# file <first_script.py>

def greet(who):
    print('Hello, {}!'.format(who))

if __name__ == '__main__':
    greet('Bob')
    greet('Ann')

Теперь наш скрипт не будет приветствовать Боба и Энн, если мы будем импортировать модуль. Это победа!

Функция main

Наш first_script.py уже достаточно хорош. Но мы можем его ещё чуть-чуть улучшить.

В теле условия if __name__… у нас перечислен набор действий, которые выполняются при запуске скрипта. Со временем таких действий может стать достаточно много. И, как вы бы догадались, может статься, что мы захотим переиспользовать и этот кусок кода! Скажу даже больше, такое происходит нередко. Поэтому существует соглашение: в теле условия if __name__… делают всего один вызов функции без аргументов main, которую объявляют выше в этом же модуле (само условие принято располагать в самом конце модуля скрипта).

С учётом всех описанных рекомендаций финальная версия скрипта first_script.py будет выглядеть так:

#!/usr/bin/env python3

def greet(who):
    print('Hello, {}!'.format(who))

def main():
    greet('Bob')
    greet('Ann')

if __name__ == '__main__':
    main()

Такой скрипт можно

  • запускать непосредственно;
  • запускать из других скриптов, вызывая функцию main;
  • использовать как библиотеку.

Запускаемые пакеты

Рассмотрим немного экзотический, но всё же встречающийся случай — запуск пакета. Могло бы показаться, что раз при загрузке пакета всегда загружается модуль __init__.py, то и функцию main, и условие нужно располагать в нём. Но авторы по ряду причин решили реализовать запуск пакетов несколько иначе: при загрузке пакета перед запуском ищется модуль __main__.py и выполняется как скрипт. Здесь мы не будем углубляться в причины, побудившие авторов языка сделать именно так, и просто запомним, что исполняемые пакеты всегда содержат скрипт __main__.py.

Когда же может понадобиться запуск пакета? Сходу можно представить такой пример. Пусть мы имели один небольшой скрипт. Со временем кода в нём становилось всё больше — настолько много, что этот скрипт стало совершенно невозможно поддерживать. Мы решили превратить один модуль в пакет, содержащий несколько. Но как такой пакет в дальнейшем запускать? Вот для этого мы и можем использовать модуль __main__.py!


Самостоятельная работа

  1. Создайте модуль module.py следующего вида: python print("__name__ == '{}'".format(__name__))
  2. В REPL импортируйте модуль: интерпретатор запускайте, находясь в директории, содержащей модуль, а в приглашении выполните import module
  3. Посмотрите, что выведется на экран.
  4. Запустите модуль на исполнение (вызовите python3 module.py из директории, в которой вы создали модуль).
  5. Посмотрите, что выводится на экран на этот раз.

Дополнительные материалы

  1. Полный список того, что добавляет в модуль машинерия импортирования
  2. Имя '__main__' и его значение

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
900
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 120 курсов, 2000+ часов теории
  • 900 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы

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

Иконка программы Python-разработчик
Профессия
Разработка веб-приложений на Django
1 декабря 8 месяцев

Используйте Хекслет по максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг»