DelegatorBot如何在TelePot中正常工作?

时间:2017-07-29 09:45:43

标签: python telegram telepot

我试图通过查看此处提供的Telepot示例https://github.com/nickoala/telepot/blob/master/examples/chat/counter.py来尝试研究python库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)

2

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

bot = telepot.DelegatorBot(TOKEN, [
    pave_event_space()(
        per_chat_id(), create_open, MessageCounter, timeout=10
    ),
])

3

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

我的意思是这一部分:

pave_event_space()(
    per_chat_id(), create_open, MessageCounter, timeout=10
)

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

pave_event_space()调用的方法是否返回对另一个方法的引用?然后使用参数(per_chat_id(), create_open, MessageCounter, timeout=10)

调用此返回的方法

1 个答案:

答案 0 :(得分:12)

简短回答

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

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

更长的答案

要了解pave_event_space()的作用以及该系列参数的含义,我们必须回到基础并理解DelegatorBot接受的参数。

DelegatorBot的构造函数是explained here。简而言之,它接受2元组(seeder function, delegate-producing function)的列表。为了减少冗长,我打算将第一个元素称为播种机,将第二个元素称为委托生产者

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

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

(注意:实际上,播种者不一定会返回number,而委托制作者也不一定会返回Thread。为了清楚起见,我在上面进行了简化。See the reference完整的解释。)

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

bot = DelegatorBot(TOKEN, [
        (per_chat_id(), create_open(MessageCounter, ...))])

后来,我向处理程序(例如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

它接受函数pair,并返回一个类似pair的函数(与pair相同的签名),但标注更复杂的播种器和更多参数。这就是我的意思"修补"。

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

bot = DelegatorBot(TOKEN, [
    include_callback_query_chat_id(
        pave_event_space())(
            per_chat_id(), create_open, Lover, timeout=10),
])

它修补事件空间的内容,然后修补回调查询内容,使播种器(per_chat_id())和处理程序(Lover)能够协同工作。

我现在可以说的全部。我希望这会对代码有所启发。祝你好运。