Задать вопрос
Lord_of_Rings
@Lord_of_Rings
Дунадан - северный странник. Злой, но очень добрый

Почему приложение Flask не работает в классе?

Вот такой код
from flask import Flask, render_template 

class Web:
    app = Flask(__name__) 

    @app.route('/') 
    def index(self):
        return render_template('index.html')

Web().app.run()


Выдаёт ошибку, что типа Web.index() принимает аргумент self
  • Вопрос задан
  • 286 просмотров
Подписаться 1 Простой 3 комментария
Решения вопроса 1
Vindicar
@Vindicar
RTFM!
Чтобы понять, нужно понимать три вещи про питон.
Во-первых, в нём всё - оператор. Да-да.
def - это оператор объявления функции.
class - оператор объявления класса.
Тебе никто не запрещает сделать
if condition:
    def foo():
        pass
else:
    def bar():
        pass


Во-вторых, все методы класса существуют на уровне класса. Явная передача self на это намекает.
Иными словами:
class Foo:
    def bar(self, baz):
        print(self, baz)

foo = Foo()
# вот этот вызов
foo.bar('hallo!')
# полностью эквивалентен вот этому
Foo.bar(foo, 'hallo!')

А когда ты обращаешься к foo.bar, Питон конструирует специальный временный объект-обёртку, который ссылается на Foo.bar(), но в то же время подставляет туда первым параметром тот объект foo, к которому произошло обращение. Так что если Foo.bar() принимает два параметра (self и baz), то foo.bar() принимает уже один (baz), так как правильный self будет подставлен этой обёрткой самостоятельно.

В-третьих, декоратор - это просто синтаксический сахар для вызова функции. Иными словами:
@app.route('/') 
def index(self):
    return render_template('index.html')

будет эквивалентно вот такому коду:
def index(self):
    return render_template('index.html')

_decorator = app.route('/')
index = _decorator(index)

Причем это будет работать одинаково и вне класса, и внутри класса.
Как это относится к твоему вопросу? А вот как.
class Web:
    app = Flask(__name__) 

    @app.route('/') 
    def index(self):
        return render_template('index.html')

Последовательность действий тут такова:
1. Создаётся пространство имён для нового класса, пока что безымянного.
2. В этом пространстве имён создаётся переменная app, её выражение вычисляется немедленно
3. В этом пространстве имён создаётся функция index()
4. Отрабатывает декоратор @app.route(), и регистрирует эту функцию index() как обработчик запроса. Обрати внимание, ни функция index(), ни @app.route понятия не имеют, что index() находится внутри класса! Поэтому app.route не ожидает, что index() будет первым параметром принимать какой-то там self. Как следствие, вместо self функция index() получит то, что передал первым параметром Flask. В данному случае - ничего, так как у тебя роут без параметров.
5. Ну и в итоге отрабатывает создание класса, с учётом указанных предков (у тебя это неявно указанный object), наполнения пространства имён, и пр. Этот класс потом присваивается переменной Web в вышележащем пространстве имён.

Ну и как теперь жить?
А очень просто. Если тебе вот позарезу нужны обработчики роутов в виде методов класса, можно сделать так:
class Web:
    def __init__(self):
        self.app = Flask(__name__) 
        # получаем обёртку для метода, привязанную к нашему self, см. пункт 2
        self_index = self.index
        # вызываем декоратор вручную, см. пункт 3.
        decorator = self.app.route('/')
        decorator(self_index)
        # ну или то же самое в одну строку:
        self.app.route('/')(self.index)

    def index(self):
        return render_template('index.html')

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

Войдите, чтобы написать ответ

Похожие вопросы