Определение основных функций в Python

Оглавление

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

Тем не менее, наличие определенной начальной точки для выполнения программы полезно для понимания того, как работает программа. Программисты Python придумали несколько соглашений для определения этой начальной точки.

К концу этой статьи вы поймете:

  • Что такое специальная переменная __name__ и как Python ее определяет
  • Почему вы захотите использовать main() в Python
  • Какие соглашения существуют для определения main() в Python
  • Какие существуют лучшие практики по определению кода, который следует поместить в main()

Основы Python main()

В некоторых скриптах Python вы можете увидеть определение функции и условный оператор, который выглядит так, как показано ниже:

def main():
    print("Hello World!")

if __name__ == "__main__":
    main()

В этом коде есть функция main(), которая печатает фразу Hello World! при выполнении интерпретатором Python. Также есть условный оператор (или if), который проверяет значение __name__ и сравнивает его со строкой "__main__". Когда значение оператора if равно True, интерпретатор Python выполняет main(). Подробнее об условных операторах вы можете прочитать в Условные операторы в Python.

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

Режимы выполнения в Python

Существует два основных способа, которыми вы можете указать интерпретатору Python выполнить или использовать код:

  1. Вы можете выполнить файл Python как скрипт с помощью командной строки.
  2. Вы можете импортировать код из одного файла Python в другой файл или в интерактивный интерпретатор.

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

Мы будем использовать этот файл-пример, сохраненный как execution_methods.py, чтобы изучить, как меняется поведение кода в зависимости от контекста:

print("This is my file to test Python's execution methods.")
print("The variable __name__ tells me which context this file is running in.")
print("The value of __name__ is:", repr(__name__))

В этом файле определены три вызова print(). Первые два выводят несколько вводных фраз. Третий print() сначала выведет фразу The value of __name__ is, а затем - представление переменной __name__ с помощью встроенной в Python функции repr().

В Python repr() отображает печатное представление объекта. В этом примере используется repr(), чтобы подчеркнуть, что значение __name__ - это строка. Подробнее о repr() можно прочитать в документации по Python.

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

  1. Файл: Как правило, файлом Python является любой файл, содержащий код. Большинство файлов Python имеют расширение .py.

  2. Скрипт: Сценарий Python - это файл, который вы собираетесь выполнить из командной строки для решения какой-либо задачи.

  3. Модуль: Модуль Python - это файл, который вы собираетесь импортировать из другого модуля или сценария, или из интерактивного интерпретатора. Подробнее о модулях вы можете прочитать в Python Modules and Packages - An Introduction.

Это различие также обсуждается в How to Run Your Python Scripts.

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

При таком подходе вы хотите выполнить свой сценарий Python из командной строки.

Когда вы выполняете скрипт, вы не можете интерактивно определять код, который выполняет интерпретатор Python. Детали того, как можно выполнять Python из командной строки, не так важны для целей этой статьи, но вы можете раскрыть рамку ниже, чтобы прочитать больше о различиях между командной строкой в Windows, Linux и macOS.

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

В Linux и macOS командная строка обычно выглядит так, как показано ниже:

eleanor@realpython:~/Documents$

Часть перед знаком доллара ($) может выглядеть по-разному, в зависимости от вашего имени пользователя и имени вашего компьютера. Команды, которые вы набираете, будут идти после $. В Linux или macOS имя исполняемого файла Python 3 - python3, поэтому для запуска скриптов Python следует набирать python3 script_name.py после $.

В Windows командная строка обычно выглядит так, как показано ниже:

C:\Users\Eleanor\Documents>

Часть перед > может выглядеть по-разному, в зависимости от вашего имени пользователя. Команды, которые вы введете, будут идти после >. В Windows имя исполняемого файла Python 3 обычно python, поэтому вы должны запускать скрипты Python, набирая python script_name.py после >.

Независимо от вашей операционной системы, вывод скриптов Python, которые вы используете в этой статье, будет одинаковым, поэтому в этой статье показан только стиль ввода для Linux и macOS, а строка ввода будет начинаться с $.

Теперь необходимо выполнить скрипт execution_methods.py из командной строки, как показано ниже:

$ python3 execution_methods.py
This is my file to test Python's execution methods.
The variable __name__ tells me which context this file is running in.
The value of __name__ is: '__main__'

В этом примере видно, что __name__ имеет значение '__main__', где символы кавычек (') говорят о том, что значение имеет строковый тип.

Помните, что в Python нет разницы между строками, определенными с помощью одинарных кавычек (') и двойных кавычек ("). Подробнее об определении строк вы можете прочитать в Основные типы данных в Python.

Вы получите идентичный результат, если включите строку shebang в свой скрипт и выполните его напрямую (./execution_methods.py) или воспользуетесь магией %run в IPython или Jupyter Notebooks.

Вы также можете увидеть, как скрипты Python выполняются из пакетов, добавляя к команде аргумент -m. Чаще всего это рекомендуется делать при использовании pip: python3 -m pip install package_name.

Добавление аргумента -m запускает код в модуле __main__.py пакета. Более подробную информацию о файле __main__.py вы можете найти в статье Как опубликовать пакет Python с открытым исходным кодом в PyPI.

Во всех трех случаях __name__ имеет одно и то же значение: строку '__main__'.

Технические подробности: В документации Python определено, когда __name__ будет иметь значение '__main__':

При чтении из стандартного ввода, скрипта или интерактивного приглашения значение модуля __name__ устанавливается равным '__main__'. ( Источник)

__name__ хранится в глобальном пространстве имен модуля вместе с __doc__, __package__ и другими атрибутами. Подробнее об этих атрибутах можно прочитать в документации Python Data Model и, специально для модулей и пакетов, в Python Import documentation.

Импорт в модуль или интерактивный интерпретатор

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

В процессе импорта Python выполняет операторы, определенные в указанном модуле (но только в первый раз, когда вы импортируете модуль). Чтобы продемонстрировать результаты импорта вашего execution_methods.py файла, запустите интерактивный интерпретатор Python, а затем импортируйте ваш execution_methods.py файл:

>>> import execution_methods
This is my file to test Python's execution methods.
The variable __name__ tells me which context this file is running in.
The value of __name__ is: 'execution_methods'

В этом выводе кода видно, что интерпретатор Python выполняет три вызова print(). Первые две строки вывода точно такие же, как и при выполнении файла как сценария в командной строке, потому что ни в одной из первых двух строк нет переменных. Однако есть разница в выводе третьей строки print().

Когда интерпретатор Python импортирует код, значение __name__ устанавливается равным имени импортируемого модуля. Это можно увидеть в третьей строке вывода выше. __name__ имеет значение 'execution_methods', которое является именем файла .py, из которого импортируется Python.

Обратите внимание, что если вы import повторно запустите модуль, не выходя из Python, вывода не будет.

Примечание: Для получения дополнительной информации о том, как работает импорт в Python, ознакомьтесь с Python import: Advanced Techniques and Tips, а также Absolute vs Relative Imports in Python.

Лучшие практики для основных функций Python

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

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

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

Поместить большую часть кода в функцию или класс

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

  • Выполнение вычислений, которые занимают много времени
  • Запись в файл на диске
  • Печать информации, которая загромождает терминал пользователя

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

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

Сохраните приведенный ниже код в файл с именем best_practices.py, чтобы продемонстрировать эту идею:

 1 from time import sleep
 2
 3 print("This is my file to demonstrate best practices.")
 4
 5 def process_data(data):
 6     print("Beginning data processing...")
 7     modified_data = data + " that has been modified"
 8     sleep(3)
 9     print("Data processing finished.")
10     return modified_data

В этом коде вы сначала импортируете sleep() из time модуля .

sleep() приостанавливает работу интерпретатора на столько секунд, сколько вы укажете в качестве аргумента, и выдает функцию, которая для данного примера выполняется очень долго. Далее вы используете print(), чтобы вывести предложение, описывающее цель этого кода.

Затем вы определяете функцию process_data(), которая делает пять вещей:

  1. Печатает некоторые выходные данные, чтобы сообщить пользователю о начале обработки данных
  2. Модифицирует входные данные
  3. Прерывает выполнение на три секунды с помощью sleep()
  4. Печатает некоторый вывод, чтобы сообщить пользователю, что обработка завершена
  5. Возвращает измененные данные

Выполните файл Best Practices в командной строке

Теперь, что произойдет, если вы выполните этот файл как сценарий в командной строке?

Интерпретатор Python выполнит строки from time import sleep и print(), которые находятся вне определения функции, затем создаст определение функции process_data(). Затем сценарий завершится, не сделав ничего дальше, потому что в сценарии нет кода, выполняющего process_data().

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

$ python3 best_practices.py
This is my file to demonstrate best practices.

Вывод, который мы видим здесь, является результатом первого print(). Обратите внимание, что импорт из time и определение process_data() не дают никакого результата. В частности, не выводятся результаты вызовов print(), которые находятся внутри определения process_data()!

Импорт файла лучших практик в другой модуль или интерактивный интерпретатор

Когда вы импортируете этот файл в интерактивную сессию (или другой модуль), интерпретатор Python выполнит точно такие же действия, как и при выполнении файла в качестве сценария.

После того как интерпретатор Python импортирует файл, вы можете использовать любые переменные, классы или функции, определенные в импортированном модуле. Чтобы продемонстрировать это, мы воспользуемся интерактивным интерпретатором Python. Запустите интерактивный интерпретатор и введите import best_practices:

>>> import best_practices
This is my file to demonstrate best practices.

Единственным результатом импорта файла best_practices.py является первый вызов print(), определенный вне process_data(). Импорт из time и определение process_data() не дают никакого вывода, как и при выполнении кода из командной строки.

Используйте if __name__ == "__main__" для управления выполнением вашего кода

Что делать, если вы хотите, чтобы process_data() выполнялся при запуске сценария из командной строки, но не когда интерпретатор Python импортирует файл?

Вы можете использовать идиому if __name__ == "__main__" для определения контекста выполнения и условно запускать process_data() только тогда, когда __name__ равен "__main__". Добавьте приведенный ниже код в нижнюю часть вашего best_practices.py файла:

11 if __name__ == "__main__":
12     data = "My data read from the Web"
13     print(data)
14     modified_data = process_data(data)
15     print(modified_data)

В этом коде вы добавили условный оператор, который проверяет значение __name__. Это условие будет иметь значение True, если __name__ равно строке "__main__". Помните, что специальное значение "__main__" для переменной __name__ означает, что интерпретатор Python выполняет ваш сценарий, а не импортирует его.

Внутри условного блока вы добавили четыре строки кода (строки 12, 13, 14 и 15):

  • Строки 12 и 13: Вы создаете переменную data, в которой хранятся данные, полученные из Web, и печатаете их.
  • Строка 14: Вы обрабатываете данные.
  • Строка 15: Вы печатаете измененные данные.

Теперь запустите ваш best_practices.py скрипт из командной строки, чтобы посмотреть, как изменится вывод:

$ python3 best_practices.py
This is my file to demonstrate best practices.
My data read from the Web
Beginning data processing...
Data processing finished.
My data read from the Web that has been modified

Во-первых, вывод показывает результат вызова print() за пределами process_data().

После этого выводится значение data. Это произошло потому, что переменная __name__ имеет значение "__main__", когда интерпретатор Python выполняет файл как сценарий, поэтому условный оператор оценивается в True.

Далее ваш скрипт вызвал process_data() и передал data для модификации. Когда process_data() выполняется, он печатает на выходе несколько сообщений о состоянии. Наконец, выводится значение modified_data.

Теперь следует проверить, что произойдет, если импортировать файл best_practices.py из интерактивного интерпретатора (или другого модуля). Пример ниже демонстрирует эту ситуацию:

>>> import best_practices
This is my file to demonstrate best practices.

Обратите внимание, что вы получаете то же поведение, что и до добавления условного оператора в конец файла! Это потому, что переменная __name__ имела значение "best_practices", поэтому Python не выполнял код внутри блока, включая process_data(), поскольку условный оператор оценивался как False.

Создайте функцию main(), содержащую код, который вы хотите запустить

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

Многие языки, такие как C, C++, Java и некоторые другие, определяют специальную функцию main(), которую операционная система автоматически вызывает при выполнении скомпилированной программы. Эту функцию часто называют точкой входа, поскольку именно с нее начинается выполнение программы.

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

Хотя Python не придает никакого значения функции с именем main(), лучше всего назвать функцию точки входа main() в любом случае. Таким образом, любой другой программист, прочитавший ваш сценарий, сразу поймет, что эта функция является начальной точкой кода, выполняющего основную задачу сценария.

Кроме того, main() должен содержать любой код, который вы хотите запустить, когда интерпретатор Python выполнит файл. Это лучше, чем помещать код непосредственно в условный блок, потому что пользователь может повторно использовать main(), если он импортирует ваш модуль.

Измените файл best_practices.py так, чтобы он выглядел так, как показано ниже:

 1 from time import sleep
 2
 3 print("This is my file to demonstrate best practices.")
 4
 5 def process_data(data):
 6     print("Beginning data processing...")
 7     modified_data = data + " that has been modified"
 8     sleep(3)
 9     print("Data processing finished.")
10     return modified_data
11
12 def main():
13     data = "My data read from the Web"
14     print(data)
15     modified_data = process_data(data)
16     print(modified_data)
17
18 if __name__ == "__main__":
19     main()

В этом примере вы добавили определение main(), включающее код, который ранее находился внутри условного блока. Затем вы изменили условный блок так, чтобы он выполнял main(). Если вы запустите этот код как сценарий или импортируете его, то получите тот же результат, что и в предыдущем разделе.

Вызов других функций из main()

Другая распространенная практика в Python - заставить main() выполнять другие функции , а не включать код, завершающий задачу, в main(). Это особенно полезно, когда вы можете составить общую задачу из нескольких небольших подзадач, которые могут выполняться независимо.

Например, у вас может быть сценарий, который выполняет следующее:

  1. Считывает файл данных из источника, которым может быть база данных, файл на диске или веб-интерфейс
  2. Обрабатывает данные
  3. Записывает обработанные данные в другое место

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

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

Измените ваш best_practices.py файл так, чтобы он выглядел так, как показано ниже:

 1 from time import sleep
 2
 3 print("This is my file to demonstrate best practices.")
 4
 5 def process_data(data):
 6     print("Beginning data processing...")
 7     modified_data = data + " that has been modified"
 8     sleep(3)
 9     print("Data processing finished.")
10     return modified_data
11
12 def read_data_from_web():
13     print("Reading data from the Web")
14     data = "Data from the web"
15     return data
16
17 def write_data_to_database(data):
18     print("Writing data to a database")
19     print(data)
20
21 def main():
22     data = read_data_from_web()
23     modified_data = process_data(data)
24     write_data_to_database(modified_data)
25
26 if __name__ == "__main__":
27     main()

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

В строке 21 определена функция main(). В этом примере вы изменили main() так, чтобы он поочередно вызывал функции чтения данных, обработки данных и записи данных.

Сначала из read_data_from_web() создается data. Этот data передается в process_data(), который возвращает modified_data. Наконец, modified_data передается в write_data_to_database().

Последние две строки скрипта - это условный блок, который проверяет __name__ и запускает main(), если утверждение if является True.

Теперь вы можете запустить весь конвейер обработки из командной строки, как показано ниже:

$ python3 best_practices.py
This is my file to demonstrate best practices.
Reading data from the Web
Beginning data processing...
Data processing finished.
Writing processed data to a database
Data from the web that has been modified

На выходе этого выполнения видно, что интерпретатор Python выполнил main(), который выполнил read_data_from_web(), process_data() и write_data_to_database(). Однако вы также можете импортировать файл best_practices.py и повторно использовать process_data() для другого источника входных данных, как показано ниже:

>>> import best_practices as bp
This is my file to demonstrate best practices.
>>> data = "Data from a file"
>>> modified_data = bp.process_data(data)
Beginning data processing...
Data processing finished.
>>> bp.write_data_to_database(modified_data)
Writing processed data to a database
Data from a file that has been modified

В этом примере вы импортировали best_practices и сократили имя до bp для этого кода.

Процесс импорта заставил интерпретатор Python выполнить все строки кода в файле best_practices.py, поэтому в выводе появилась строка, объясняющая назначение этого файла.

Затем вы сохранили данные из файла в data вместо того, чтобы читать их из Web. Затем вы повторно использовали process_data() и write_data_to_database() из файла best_practices.py. В этом случае вы воспользовались преимуществом повторного использования кода вместо того, чтобы определять всю логику в main().

Краткое описание лучших практик использования главной функции Python

Вот четыре ключевых лучших практики main() в Python, которые вы только что видели:

  1. Поместите код, который долго выполняется или имеет другие последствия для компьютера, в функцию или класс, чтобы вы могли точно контролировать время выполнения этого кода.

  2. Используйте различные значения __name__ для определения контекста и изменения поведения вашего кода с помощью условного оператора.

  3. Вам следует назвать свою функцию точки входа main(), чтобы передать замысел функции, хотя Python не придает никакого особого значения функции с именем main().

  4. Если вы хотите повторно использовать функциональность из вашего кода, определите логику в функциях вне main() и вызывайте эти функции внутри main().

Заключение

Поздравляем! Теперь вы знаете, как создавать функции Python main().

Вы узнали следующее:

  • Знание значения переменной __name__ важно для написания кода, который служит двойной цели - исполняемого скрипта и импортируемого модуля.

  • __name__ принимает различные значения в зависимости от того, как вы выполнили ваш Python-файл. __name__ будет равно:

    • "__main__" при выполнении файла из командной строки или с помощью python -m (для выполнения __main__.py файла пакета)
    • Имя модуля, если модуль импортируется
  • Программисты Python разработали набор хороших практик, которые следует использовать, когда вы хотите разрабатывать многократно используемый код.

Теперь вы готовы к написанию потрясающего кода функций на Python main()!

Back to Top