不能在从父类继承的子类上使用装饰器,但可以在对象本身上使用。

huangapple go评论60阅读模式
英文:

Cannot use a decorator on a child class inheriting from a parent class but can use it on the object itself

问题

I am creating a class inheriting from a parent class. The parent class is Telebot and the child class is my own Telegram BOT class. I am doing this to create a standard BOT that multiple scripts will call and implement with default actions etc.

Then I am trying to implement a decorator to treat default incoming messages on this child class. Here is how I am doing it (file is telegram.py):

# Import the library I will use to create my BOT
import telebot

class TelegramBot(telebot.TeleBot):
    """
    This class is called to create an instance of a Telegram BOT.
    It inherits from the Telebot class of the telebot module.
    Doc: https://pytba.readthedocs.io/en/latest/sync_version
    """

    def __init__(self, token):
        """This function is called when the class is instantiated.
        It inherits the attributes of telebot."""
        super().__init__(token)

    def polling(self):
        """This method is being extended with some custom functionality
        for the bot, instead of being overridden/polymorphed."""
        super().polling()

    @telebot.TeleBot.message_handler(commands=["testar_bot"])
    def resposta_teste(self, mensagem):
        """This method uses a decorator to have a new characteristic and functionality,
         which defines the pattern for each received message.
         The method tests the default response to test the BOT."""
        self.reply_to(mensagem, "Hello! Test passed. Everything is working :)")

When doing it like this, I receive the error:
TypeError: TeleBot.message_handler() missing 1 required positional argument: 'self'

The weirdest thing is that if I remove the decorator alongside the method and implement it in my bot object on my main.py file, it works like a charm.

Here is the code that works - I remove the decorator from the class and put it in main.py:

import telegram

bot = telegram.TelegramBot("blablabla_mytokenhidden")

@bot.message_handler(commands=["testar_bot"])
def resposta_teste(mensagem):
    """This method uses a decorator to have a new characteristic and functionality,
     which defines the pattern for each received message.
     The method tests the default response to test the BOT."""
    bot.reply_to(mensagem, "Hello! Test passed. Everything is working :)")

bot.polling()

This does not give me any error, and the decorator works like a charm. I really don't understand the logic behind this.

Why does the decorator I used on my object work, but on my child class (telegram.py with TelegramBot class) doesn't?

Edit #1

Following the object logic, I tried replacing telebot.TeleBot with self, also tested with object and it still did not work...

So I am clearly NOT getting the logic here.

英文:

I am creating a class inheriting from a parent class. The parent class is Telebot and the child class is my own Telegram BOT class. I am doing this to create a standard BOT that multiple scripts will call and implement with default actions etc.

Then I am trying to implement a decorator to treat default incoming messages on this child class. Here is how I am doing it (file is 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 :)")

When doing it like this, I receive the error:
TypeError: TeleBot.message_handler() missing 1 required positional argument: 'self'

The weirdest thing is that if I remove the decorator alongside the method and implement in my bot object on my main.py file, it works like a charm.

Here is the code that works - I remove the decorator from the class and put it on 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()

This does not gives me any error, and the decorator works like a charm. I really don't understand the logic behind this.

Why the decorator I used on my object works but on my child class (telegram.py with TelegramBot class) doesn't?

Edit #1

Following the object logic, I tried replacing telebot.TeleBot with self, also tested with object and it still did not work...

So I am clearly NOT getting the logic here.

答案1

得分: 1

在类中定义的方法在调用时需要将类的实例作为第一个参数(self)传递进去(当使用点符号表示法调用时,这会由系统自动完成,即instance.method())。@telebot.TeleBot.message_handler() 装饰器在类定义期间调用,在有任何实例之前调用。因此,处理程序不知道它附加到哪个实例,这就是为什么它要求你手动传递 self 的原因。当然,机器人框架并没有设置好这一点。

当处理程序在类外部定义时,这并不是问题,因为它不需要类的实例,也没有附加到类上。

一个解决方法是在创建实例时应用装饰器:

def __init__(self, token):
        """This function is called when the class is instantiated.
        Inherits the attributes of 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):
        """This method uses the Decorator to have a new feature and functionality,
         defining the pattern for each received message.
         The method tests the default response to test the BOT.
         """

        self.reply_to(mensagem, "Hello! Test passed. Everything is working :)")
英文:

A method defined in a class requires an instance of the class to be passed as the first argument (self) when it is called. (This is done for you when you invoke it using dot notation, i.e. instance.method().) The decorator @telebot.TeleBot.message_handler() is called during class definition, before there are any instances. Therefore the handler doesn't know what instance it is attached to, which is why it wants you to pass self in yourself. Which of course the bot framework is not set up to do.

Naturally, this isn't a problem when the handler is defined outside the class because it doesn't require an instance of the class it's attached to, not being attached to a class and all.

One solution is to instead apply the decorator when an instance is created:

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)

This makes the handler a bound method of its instance, with self already baked in. Note that this might cause problems if you create multiple instances of the class, because you'll have multiple handlers attached to the same event. The framework you're using might be OK with that, or it might not.

Another thing you can do, if you won't have multiple instances of the bot (or if the handler doesn't need to know what instance it's attached to) is to make the method a static method. Then it behaves as a regular function (albeit in the class namespace) and doesn't need to be passed an instance.

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 :)")


</details>



huangapple
  • 本文由 发表于 2023年2月10日 04:05:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/75403887.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定