Я создаю класс, наследуемый от родительского класса. Родительский класс — это Telebot, а дочерний класс — это мой собственный класс Telegram BOT. Я делаю это, чтобы создать стандартный BOT, который будет вызываться и реализовываться несколькими сценариями с действиями по умолчанию и т. д.
Затем я пытаюсь реализовать декоратор для обработки входящих сообщений по умолчанию в этом дочернем классе. Вот как я это делаю (файл telegram.py
):
# Importo a biblioteca que vou usar pra criar meu BOT
import telebot
class TelegramBot(telebot.TeleBot):
"""
Essa classe é chamada para criar uma instância de um BOT do Telegram.
Ela herda a classe Telebot do módulo telebot.
Doc: https://pytba.readthedocs.io/en/latest/sync_version
"""
def __init__(self, token):
"""Esta função é chamada no momento que a classe é instanciada.
Herda os atributos do telebot"""
super().__init__(token)
def polling(self):
"""Este método está sendo incrementado com algumas funcionalidades
próprias do bot, ao invés de ser feito o override/polimorfismo."""
super().polling()
@telebot.TeleBot.message_handler(commands=["testar_bot"])
def resposta_teste(self, mensagem):
"""Esse método utiliza do Decorator para ter uma nova caracteristica e funcionalidade,
que define o padrão para cada mensagem recebida.
O método testa a resposta padrão para fazer o teste no BOT.
"""
self.reply_to(mensagem, "Olá! Teste passou. Tudo funcionando :)")
Делая это так, я получаю сообщение об ошибке:
TypeError: TeleBot.message_handler() missing 1 required positional argument: 'self'
Самое странное, что если я удалю декоратор вместе с методом и реализую его в моем объекте бота в моем файле main.py
, он будет работать как шарм.
Вот код, который работает — я удаляю декоратор из класса и ставлю его main.py
:
import telegram
bot = telegram.TelegramBot("blablabla_mytokenhidden")
@bot.message_handler(commands=["testar_bot"])
def resposta_teste(mensagem):
"""Esse método utiliza do Decorator para ter uma nova caracteristica e funcionalidade,
que define o padrão para cada mensagem recebida.
O método testa a resposta padrão para fazer o teste no BOT.
"""
bot.reply_to(mensagem, "Olá! Teste passou. Tudo funcionando :)")
bot.polling()
Это не дает мне никаких ошибок, и декоратор работает как шарм. Я действительно не понимаю логики этого.
Почему декоратор, который я использовал для своего объекта, работает, а для моего дочернего класса (telegram.py с классом TelegramBot) — нет?
Изменить №1
Следуя логике объекта, я попытался заменить telebot.TeleBot
на self
, также проверил с object
, и это все равно не сработало...
Так что я явно НЕ понимаю логику здесь.
Метод, определенный в классе, требует, чтобы экземпляр класса передавался в качестве первого аргумента (self
) при его вызове. (Это делается для вас, когда вы вызываете его с помощью записи через точку, т.е. instance.method()
.) Декоратор @telebot.TeleBot.message_handler()
вызывается во время определения класса, до того, как появятся какие-либо экземпляры. Поэтому обработчик не знает, к какому экземпляру он подключен, поэтому он хочет, чтобы вы передали self
в себя. На что, конечно же, бот-фреймворк не настроен.
Естественно, это не проблема, когда обработчик определен вне класса, потому что он не требует экземпляра класса, к которому он присоединен, не привязан к классу и все такое.
Одним из решений является применение декоратора при создании экземпляра:
def __init__(self, token):
"""Esta função é chamada no momento que a classe é instanciada.
Herda os atributos do telebot"""
super().__init__(token)
self.resposta_teste = telebot.TeleBot.message_handler(
commands=["testar_bot"])(self. resposta_teste)
Это делает обработчик связанным методом своего экземпляра с уже запеченным self
. Обратите внимание, что это может вызвать проблемы, если вы создадите несколько экземпляров класса, поскольку к одному и тому же событию будет прикреплено несколько обработчиков. Фреймворк, который вы используете, может быть в порядке с этим, а может и нет.
Еще одна вещь, которую вы можете сделать, если у вас не будет нескольких экземпляров бота (или если обработчику не нужно знать, к какому экземпляру он подключен), — это сделать метод статическим. Затем она ведет себя как обычная функция (хотя и в пространстве имен класса) и не нуждается в передаче экземпляра.
class TelegramBot(telebot.TeleBot):
# ...
@telebot.TeleBot.message_handler(commands=["testar_bot"])
@staticmethod
def resposta_teste(mensagem):
"""Esse método utiliza do Decorator para ter uma nova caracteristica e funcionalidade,
que define o padrão para cada mensagem recebida.
O método testa a resposta padrão para fazer o teste no BOT.
"""
self.reply_to(mensagem, "Olá! Teste passou. Tudo funcionando :)")
Декораторы — это круто, но вам нужно, чтобы сами функции были значениями, которые вы можете просто передавать, как строки или числа. Декоратор — это просто функция, которая принимает функцию и возвращает функцию, со специальным синтаксисом для ее вызова в точке определения функции. (Или класс. Вы можете написать декоратор, который украшает класс, и сам декоратор может быть классом.)
Я читаю, что такое декораторы и как они работают... Довольно аккуратно, но сложно. Хорошо. Оба ваших решения сработали и помогли мне немного лучше понять эту концепцию. Спасибо @kindall!