Что такое __name__ в Flask

Вы когда-нибудь задавались вопросом, почему при создании приложения Python Flask вы передаете __name__ классу Flask?

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

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

Лучший способ понять это — увидеть на практике.

Итак, давайте сделаем это!

Базовый пример того, как __name__ работает с Flask

Создайте новый каталог с именем flask-example, затем внутри этого каталога создайте виртуальную среду с именем venv и активируйте ее.

$ mkdir flask-example
$ cd flask-example 
$ python3 -m venv venv
$. venv/bin/activate
(venv) $ 

Примечание: я создал отдельный учебник, который расскажет вам больше о виртуальных средах Python.

Затем обновите pip…

(venv) $ python3 -m pip install --upgrade pip
Collecting pip
  Using cached pip-21.3.1-py3-none-any.whl (1.7 MB)
Installing collected packages: pip
  Attempting uninstall: pip
    Found existing installation: pip 20.1.1
    Uninstalling pip-20.1.1:
      Successfully uninstalled pip-20.1.1
Successfully installed pip-21.3.1

…и установите Flask в вашей виртуальной среде с помощью pip:

(venv) $ pip install flask
Collecting flask
  Using cached Flask-2.0.2-py3-none-any.whl (95 kB)
Collecting click>=7.1.2
  Using cached click-8.0.3-py3-none-any.whl (97 kB)
Collecting Jinja2>=3.0
  Using cached Jinja2-3.0.2-py3-none-any.whl (133 kB)
Collecting itsdangerous>=2.0
  Using cached itsdangerous-2.0.1-py3-none-any.whl (18 kB)
Collecting Werkzeug>=2.0
  Using cached Werkzeug-2.0.2-py3-none-any.whl (288 kB)
Collecting MarkupSafe>=2.0
  Using cached MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl (13 kB)
Installing collected packages: click, MarkupSafe, Jinja2, itsdangerous, Werkzeug, flask
Successfully installed Jinja2-3.0.2 MarkupSafe-2.0.1 Werkzeug-2.0.2 click-8.0.3 flask-2.0.2 itsdangerous-2.0.1

Теперь создайте файл app.py прямо внутри каталога flask-example. Этот файл будет иметь следующее содержимое:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def example():
    return 'The value of __name__ is {}'.format(__name__)

В первой строке мы импортируем класс Flask из модуля Flask.

Затем вы создаете свое приложение и передаете переменную __name__ (произносится как «дандер метод») классу Flask.

Маршрут Flask возвращает значение __name__, чтобы мы могли проверить его значение при запуске приложения Flask.

В операторе return мы использовали метод форматирования строки Python.

Чтобы запустить приложение, выполните следующую команду внутри каталога flask-example:

(venv) $ flask run 
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Если вы перейдете по адресу http://127.0.0.1:5000/ в своем браузере, вы увидите следующее сообщение:

The value of __name__ is app

Значение __name__ — app, имя текущего модуля Python.

Каково значение __name__, когда приложение Flask находится внутри пакета?

Давайте посмотрим, как изменится значение __name__, если мы переместим app.py внутрь пакета с именем example1.

Чтобы создать пакет example1, вам необходимо сделать две вещи:

  1. Создайте каталог с именем example1 в каталоге flask-example.
  2. Создайте файл с именем __init__.py внутри каталога example1.
(venv) $ mkdir example1
(venv) $ touch example1/__init__.py

Затем переместите app.py внутрь нового пакета:

(venv) $ mv app.py example1/
(venv) $ ls example1 
__init__.py	app.py

Запустите приложение Flask, выполнив команду «flask run» из каталога example1.

(venv) $ cd example1 
(venv) $ flask run

Сообщение, которое вы видите в браузере при доступе к http://127.0.0.1:5000/, изменилось…

The value of __name__ is example1.app

Значение __name__ теперь равно example1.app.

Каково значение __name__, если приложение Flask определено в __init__.py пакета?

Давайте сделаем небольшое изменение по сравнению с предыдущим примером:

  • Переместите код из app.py в __init__.py в пакете example1.
  • Удалите весь код из файла app.py.

Это можно сделать с помощью одной команды:

(venv) $ mv app.py __init__.py

Вот что происходит при попытке запустить приложение Flask:

(venv) $ pwd
/Coding/Python/Tutorials/flask-example/example1
(venv) $ flask run
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
Usage: flask run [OPTIONS]

Error: Could not locate a Flask application. You did not provide the "FLASK_APP" environment variable, and a "wsgi.py" or "app.py" module was not found in the current directory.

Согласно ошибке, Flask не может определить приложение для выполнения. Как вы можете видеть из ошибки, Flask ищет либо:

  • Переменная среды FLASK_APP (которая не установлена).
  • Модуль в текущем каталоге с именем wsgi.py или app.py.

Давайте используем команду export, чтобы сообщить Flask, что приложение находится в текущем пакете, используя переменную среды FLASK_APP.

(venv) $ pwd
/Coding/Python/Tutorials/flask-example/example1
(venv) $ export FLASK_APP=.
(venv) $ flask run         
 * Serving Flask app "."
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Сообщение, возвращаемое нашим приложением:

The value of __name__ is example1

А что, если мы хотим запустить приложение из главного каталога нашего проекта? (родительский каталог example1).

(venv) $ cd..
(venv) $ pwd
/Coding/Python/Tutorials/flask-example

В этом случае нам необходимо установить значение FLASK_APP на имя пакета: example1.

(venv) $ export FLASK_APP=example1
(venv) $ flask run                
 * Serving Flask app "example1"
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Как и ожидалось, значение __name__ не изменится, учитывая, что наше приложение Flask все еще находится в пакете example1:

The value of __name__ is example1

Что означает __name__ при запуске приложения Flask?

Согласно этому руководству Flask, переменная __name__ используется для указания местонахождения приложения. Это необходимо для установки некоторых путей, используемых приложениями Flask.

Давайте посмотрим, что это за пути, углубившись в код Flask

Прежде всего, вот как описывается первый параметр класса Flask:

:param import_name: the name of the application package

Это довольно очевидно…

Первый параметр, принимаемый конструктором класса Flask, — это имя пакета приложения. Вы можете передать его с помощью переменной __name__ (dunder name).

В строке документации класса Flask я вижу следующий раздел о первом параметре:

Что такое __name__ в Flask

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

Интересно, что там говорится, что можно передавать значение __name__ как параметр import_name, если вы запускаете свое приложение в одном модуле. Например, так, как мы это сделали в первом примере этого руководства.

Но там также говорится, что…

Если мы запускаем наше приложение в модуле внутри пакета (например, yourapplication/app.py), хорошей практикой будет передать имя пакета в качестве первого параметра вместо __name__ из-за принципа работы некоторых расширений (например, Flask-SQLAlchemy)…

…если только вы не хотите извлечь имя пакета из __name__, как показано в строке документации выше.

Зачем вы передаете __name__ классу Flask?

Мы увидели, что Flask использует значение переменной __name__ для идентификации ресурсов, используемых вашим приложением Flask.

Но для каких ресурсов Flask использует __name__?

В предыдущем разделе мы увидели, что имя первого параметра, передаваемого при создании экземпляра класса Flask, называется import_name.

Вот пример использования этого параметра в коде Flask.

Метод auto_find_instance_path класса Flask

def auto_find_instance_path(self) -> str:
        """Tries to locate the instance path if it was not provided to the
        constructor of the application class.  It will basically calculate
        the path to a folder named ``instance`` next to your main file or
        the package.
       .. versionadded:: 0.8
        """
        prefix, package_path = find_package(self.import_name)
        if prefix is None:
            return os.path.join(package_path, "instance")
        return os.path.join(prefix, "var", f"{self.name}-instance")

По сути, значение __name__, которое мы передаем в качестве первого аргумента, используется для определения местоположения папки экземпляра, если мы не передадим его конструктору класса приложения Flask (через параметр instance_path).

Папка экземпляра используется во Flask для хранения файлов конфигурации или ресурсов, которые изменяются во время выполнения. Она не должна находиться под контролем версий, и ее содержимое зависит от развертывания.

Давайте посмотрим, что возвращает этот метод, создав приложение Flask в оболочке Python.

Из каталога flask-example откройте оболочку Python и импортируйте пакет example1.

(venv) $ pwd
/Coding/Python/Tutorials/flask-example
(venv) $ python
Python 3.8.5 (default, Sep  4 2020, 02:22:02) 
[Clang 10.0.0 ]:: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import example1

Используйте функцию dir(), чтобы просмотреть свойства и методы, доступные в объекте example1.

>>> dir(example1)
['Flask', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'app', 'example']

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

>>> example1.__name__
'example1'

Давайте вызовем метод auto_find_instance_path(), используя объект app (экземпляр класса Flask).

>>> type(example1.app)
<class 'flask.app.Flask'>
>>> example1.app.auto_find_instance_path()
'/Coding/Python/Tutorials/flask-example/instance'

Это то место, где Flask создает папку экземпляра. Рядом с пакетом, где находится наше приложение Flask.

Каково значение __name__ при непосредственном запуске приложения Flask?

Для этого последнего упражнения переместите содержимое __init__.py в файл app.py внутри пакета example1.

Не удаляйте файл __init__.py.

На этом этапе __init__.py будет пустым, а содержимое app.py будет следующим:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def example():
    return 'The value of __name__ is {}'.format(__name__)

Теперь попробуйте запустить ваше приложение из каталога example1, вызвав app.py напрямую:

(venv) $ pwd
/Coding/Python/Tutorials/flask-example/example1
(venv) $ python app.py
(venv) $

Заметили, что ничего не происходит?

…это потому, что в нашем коде нет строки, которая запускает приложение.

Давайте добавим его в конец app.py:

app.run()

и затем снова запустите приложение:

(venv) $ python app.py
 * Serving Flask app 'app' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

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

The value of __name__ is __main__

На этот раз значение __name__ равно __main__ (dunder main), поскольку мы вызвали наше приложение напрямую.

Заключение

Надеюсь, теперь стало понятнее, почему __name__ передается при создании приложения Flask.

Мы увидели, как значение __name__ меняется в зависимости от того, как мы структурируем наше приложение Flask: внутри модуля или пакета.

И теперь пришло время вам создать свое приложение с использованием Flask. Вы готовы?

Автор

Фото аватара

Владимир Михайлов

Программист на Python с большим количеством опыта и разнообразных проектов.