DelegatorBot如何在TelePot中正常工作?

Ces*_*sco 4 python telegram telepot

我正在尝试Telepot通过查看counter.py此处提供的示例来研究python库:https://github.com/nickoala/telepot/blob/master/examples/chat/counter.py.
我发现有点难以理解DelegatorBot课程的实际运作方式.

这是我认为到目前为止我所理解的:

1.

我看到最初定义了这个类(派生自"ChatHandler"类):

class MessageCounter(telepot.helper.ChatHandler):

    def __init__(self, *args, **kwargs):
        super(MessageCounter, self).__init__(*args, **kwargs)
        self._count = 0

    def on_chat_message(self, msg):
        self._count += 1
        self.sender.sendMessage(self._count)
Run Code Online (Sandbox Code Playgroud)

2.

然后通过实例化类来创建机器人DelegatorBot:

bot = telepot.DelegatorBot(TOKEN, [
    pave_event_space()(
        per_chat_id(), create_open, MessageCounter, timeout=10
    ),
])
Run Code Online (Sandbox Code Playgroud)

3.

我知道DelegatorBot创建了一个新实例并将其放入变量中bot.第一个参数是电报验证此机器人所需的令牌,第二个参数是包含我不理解的内容的列表.

我的意思是这部分:

pave_event_space()(
    per_chat_id(), create_open, MessageCounter, timeout=10
)
Run Code Online (Sandbox Code Playgroud)

然后我的问题是......

pave_event_space()称为一个方法,它返回到另一种方法的参考?然后使用参数调用此返回的方法(per_chat_id(), create_open, MessageCounter, timeout=10)

Nic*_*Lee 12

简短的回答

是的,pave_event_space()返回一个函数.我们称之为fn.fn然后调用fn(per_chat_id(), create_open, ...),返回一个2元组(seeder function, delegate-producing function).

如果你想进一步研究代码,这个简短的答案可能不是很有帮助...

更长的答案

要理解pave_event_space()这一系列论点的含义和含义,我们必须回到基础并理解DelegatorBot接受什么作为参数.

DelegatorBot这里解释了构造函数.简单地说,它接受一个2元组的列表(seeder function, delegate-producing function).为了减少冗长,我将调用第一个元素播种器和第二个元素delegate-producer.

播种者有这个签名seeder(msg) -> number.对于收到的每条消息,seeder(msg)都会调用它来生成一个number.如果number是新的,将调用伴随委托生成器(与播种器共享相同元组的那个)以生成线程,该线程用于处理新消息.如果它number已被正在运行的线程占用,则不执行任何操作.从本质上讲,播种机会对信息进行"分类".如果它看到一条属于新"类别"的消息,它会生成一个新线程.

代表制作人有此签名producer(cls, *args, **kwargs) -> Thread.它调用cls(*args, **kwargs)实例化一个处理程序对象(MessageCounter在您的情况下)并将其包装在一个线程中,因此处理程序的方法是独立执行的.

(注意:实际上,播种器不一定返回a number并且委托生产者不一定返回a Thread.为了清楚起见,我在上面进行了简化.请参阅参考资料以获得完整的解释.)

在遥控器的早期阶段,DelegatorBot通常通过透明地提供播种机和代表制作人来制作:

bot = DelegatorBot(TOKEN, [
        (per_chat_id(), create_open(MessageCounter, ...))])
Run Code Online (Sandbox Code Playgroud)

后来,我向处理程序(例如ChatHandler)添加了生成自己的事件(例如,超时事件)的功能.每处理程序都有自己的事件空间,因此不同类的事件不会混合.在每个事件空间中,事件对象本身也有一个源id,用于标识哪个处理程序已发出它.这种架构对于播种机和委托生产者提出了一些额外的要求.

播种机必须能够"分类"事件(除了外部消息之外)并返回number导致事件发射器的事件(因为我们不想为此事件生成线程;它应该由事件处理发射器本身).委托生成者还必须将适当的事件空间传递给Handler类(因为每个Handler类都获得一个外部生成的唯一事件空间).

为了使一切正常工作,必须向播种机及其配套代表生产者提供相同的事件空间.每一对(seeder, delegate-producer)都必须获得一个全球独一无二的活动空间.pave_event_space()确保这两个条件,基本上补充一些额外的操作和参数per_chat_id(),create_open()并确保它们是一致的.

更深

究竟如何"修补"?为什么我会让你做pave_event_space()(...)而不是更直接pave_event_space(...)

首先,回想一下我们的最终目标是拥有一个2元组(per_chat_id(), create_open(MessageCounter, ...)).要"修补"它通常意味着(1)附加一些额外的操作per_chat_id(),以及(2)在调用中插入一些额外的参数create_open(... more arguments here ...).这意味着我不能让用户create_open(...)直接调用,因为一旦调用它,我就无法插入额外的参数.我需要一个更抽象的结构,用户在其中指定,create_open但调用create_open(...)实际上是由我完成的.

想象一个名为的函数pair,其签名是pair(per_chat_id(), create_open, ...) -> (per_chat_id(), create_open(...)).换句话说,它将第一个参数作为第一个元组元素传递,并通过对create_open(...)剩余参数进行实际调用来创建第二个元组元素.

现在,它达到了我无法用语言解释源代码的程度(我一直在思考30分钟).pave_event_space看起来像这样的伪代码:

def pave_event_space(fn=pair):
    def p(s, d, *args, **kwargs):
        return fn(append_event_space_seeder(s), 
                  d, *args, event_space=event_space, **kwargs)
    return p
Run Code Online (Sandbox Code Playgroud)

它接受函数pair,并返回一个类似pair函数(签名相同pair),但有一个更复杂的播种器和更多参数标记.这就是我所说的"补丁".

pave_event_space是最常见的"修补者".其他修补程序包括include_callback_query_chat_idintercept_callback_query_origin.它们基本上都做同样的事情:采用类似pair函数,返回pair类似函数,使用更复杂的播种器和更多参数标记.由于输入和输出相似,因此可以链接它们以应用多个补丁.如果您查看回调示例,您将看到如下内容:

bot = DelegatorBot(TOKEN, [
    include_callback_query_chat_id(
        pave_event_space())(
            per_chat_id(), create_open, Lover, timeout=10),
])
Run Code Online (Sandbox Code Playgroud)

它修补事件空间的东西,然后修补回调查询的东西,使seeder(per_chat_id())和handler(Lover)能够内聚地工作.

这就是我现在所能说的.我希望这会对代码有所启发.祝好运.