DelegatorBot 在 TelePot 中是如何工作的?
How does the DelegatorBot work exactly in TelePot?
我正在尝试通过查看此处提供的 counter.py
示例来研究 python 库 Telepot
:https://github.com/nickoala/telepot/blob/master/examples/chat/counter.py.
我发现有点难以理解 DelegatorBot
class 的实际工作原理。
这是我到目前为止所理解的:
1.
我看到最初这个 class(派生自 "ChatHandler" class)正在被定义:
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.
然后通过实例化 class 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()
调用的方法是 returns 对另一个方法的引用吗?然后使用参数 (per_chat_id(), create_open, MessageCounter, timeout=10)
?
调用这个返回的方法
简答
是的,pave_event_space()
returns一个函数。我们称之为 fn
。 fn
然后用 fn(per_chat_id(), create_open, ...)
调用,其中 returns 一个二元组 (seeder function, delegate-producing function)
.
如果您想进一步研究代码,这个简短的回答可能不是很有帮助...
更长的答案
要了解 pave_event_space()
的作用以及这一系列参数的含义,我们必须回到基础并了解 DelegatorBot
接受的参数。
DelegatorBot
的构造函数是explained here。简单地说,它接受一个 2 元组 (seeder function, delegate-producing function)
的列表。为了减少冗长,我将调用第一个元素 seeder 和第二个元素 delegate-producer.
一个播种者有这个签名seeder(msg) -> number
。对于收到的每条消息,都会调用 seeder(msg)
以生成 number
。如果 number
是新的,则将调用伴随的委托生产者(与播种者共享同一个元组的那个)来产生一个线程,该线程用于处理新消息。如果 number
已被 运行 线程占用,则什么都不做。实质上,播种者 "categorizes" 消息。如果它看到属于新 "category".
的消息,它会生成一个新线程
委托制作人具有此签名 producer(cls, *args, **kwargs) -> Thread
。它调用 cls(*args, **kwargs)
来实例化一个处理程序对象(在你的例子中是 MessageCounter
)并将其包装在一个线程中,因此处理程序的方法是独立执行的。
(注意:实际上,播种者不一定 returns 和 number
委托制作人不一定 returns 和 Thread
。我简化了上面是为了清楚。See the reference 以获得完整的解释。)
在 telepot 的早期,DelegatorBot
通常是通过透明地提供播种机和委托制作人来实现的:
bot = DelegatorBot(TOKEN, [
(per_chat_id(), create_open(MessageCounter, ...))])
后来,我向处理程序(例如 ChatHandler
)添加了生成自己的事件(例如,超时事件)的功能。每个 class 的处理程序都有自己的 事件 space,所以不同的 classes 事件不会混合。在每个事件 space 中,事件对象本身也有一个 source id 来标识哪个处理程序发出了它。这种架构对播种者和委托生产者提出了一些额外的要求。
Seeders 必须能够 "categorize" 事件(除了外部消息)和 returns 相同的 number
导致事件发射器(因为我们不希望为此事件生成一个线程;它应该由事件发射器本身处理)。委托生产者还必须将适当的事件 space 传递给处理程序 class(因为每个处理程序 class 都会获得一个在外部生成的唯一事件 space)。
为了使一切正常工作,必须向播种机及其伙伴委托制作人提供相同的事件 space。并且每一对 (seeder, delegate-producer)
都必须获得一个全局唯一事件 space。 pave_event_space()
保证了这两个条件,基本上是在 per_chat_id()
和 create_open()
上修补了一些额外的操作和参数,并确保它们是一致的。
更深
"patching"究竟是如何完成的?为什么我让你做 pave_event_space()(...)
而不是更直接的 pave_event_space(...)
?
首先,回想一下我们的最终目标是拥有一个 2 元组 (per_chat_id(), create_open(MessageCounter, ...))
。对于 "patch",它通常意味着 (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
,并且 returns 是一个类似于 pair
的函数(签名与 pair
相同),但具有更复杂的播种器和更多参数标记上。这就是我所说的 "patching".
pave_event_space
是最常见的 "patcher"。其他修补程序包括 include_callback_query_chat_id
和 intercept_callback_query_origin
。它们基本上都做同样的事情:接受一个类似 pair
的函数,returns 另一个类似 pair
的函数,带有更复杂的播种器和更多标记的参数。因为输入和输出相似,所以可以将它们链接起来应用多个补丁。如果你查看 callback examples,你会看到类似这样的内容:
bot = DelegatorBot(TOKEN, [
include_callback_query_chat_id(
pave_event_space())(
per_chat_id(), create_open, Lover, timeout=10),
])
它修补事件 space 的东西,然后修补回调查询的东西,使播种器 (per_chat_id()
) 和处理程序 (Lover
) 能够协同工作。
我现在只能说这些了。我希望这对代码有所启发。祝你好运。
我正在尝试通过查看此处提供的 counter.py
示例来研究 python 库 Telepot
:https://github.com/nickoala/telepot/blob/master/examples/chat/counter.py.
我发现有点难以理解 DelegatorBot
class 的实际工作原理。
这是我到目前为止所理解的:
1.
我看到最初这个 class(派生自 "ChatHandler" class)正在被定义:
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.
然后通过实例化 class 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()
调用的方法是 returns 对另一个方法的引用吗?然后使用参数 (per_chat_id(), create_open, MessageCounter, timeout=10)
?
简答
是的,pave_event_space()
returns一个函数。我们称之为 fn
。 fn
然后用 fn(per_chat_id(), create_open, ...)
调用,其中 returns 一个二元组 (seeder function, delegate-producing function)
.
如果您想进一步研究代码,这个简短的回答可能不是很有帮助...
更长的答案
要了解 pave_event_space()
的作用以及这一系列参数的含义,我们必须回到基础并了解 DelegatorBot
接受的参数。
DelegatorBot
的构造函数是explained here。简单地说,它接受一个 2 元组 (seeder function, delegate-producing function)
的列表。为了减少冗长,我将调用第一个元素 seeder 和第二个元素 delegate-producer.
一个播种者有这个签名seeder(msg) -> number
。对于收到的每条消息,都会调用 seeder(msg)
以生成 number
。如果 number
是新的,则将调用伴随的委托生产者(与播种者共享同一个元组的那个)来产生一个线程,该线程用于处理新消息。如果 number
已被 运行 线程占用,则什么都不做。实质上,播种者 "categorizes" 消息。如果它看到属于新 "category".
委托制作人具有此签名 producer(cls, *args, **kwargs) -> Thread
。它调用 cls(*args, **kwargs)
来实例化一个处理程序对象(在你的例子中是 MessageCounter
)并将其包装在一个线程中,因此处理程序的方法是独立执行的。
(注意:实际上,播种者不一定 returns 和 number
委托制作人不一定 returns 和 Thread
。我简化了上面是为了清楚。See the reference 以获得完整的解释。)
在 telepot 的早期,DelegatorBot
通常是通过透明地提供播种机和委托制作人来实现的:
bot = DelegatorBot(TOKEN, [
(per_chat_id(), create_open(MessageCounter, ...))])
后来,我向处理程序(例如 ChatHandler
)添加了生成自己的事件(例如,超时事件)的功能。每个 class 的处理程序都有自己的 事件 space,所以不同的 classes 事件不会混合。在每个事件 space 中,事件对象本身也有一个 source id 来标识哪个处理程序发出了它。这种架构对播种者和委托生产者提出了一些额外的要求。
Seeders 必须能够 "categorize" 事件(除了外部消息)和 returns 相同的 number
导致事件发射器(因为我们不希望为此事件生成一个线程;它应该由事件发射器本身处理)。委托生产者还必须将适当的事件 space 传递给处理程序 class(因为每个处理程序 class 都会获得一个在外部生成的唯一事件 space)。
为了使一切正常工作,必须向播种机及其伙伴委托制作人提供相同的事件 space。并且每一对 (seeder, delegate-producer)
都必须获得一个全局唯一事件 space。 pave_event_space()
保证了这两个条件,基本上是在 per_chat_id()
和 create_open()
上修补了一些额外的操作和参数,并确保它们是一致的。
更深
"patching"究竟是如何完成的?为什么我让你做 pave_event_space()(...)
而不是更直接的 pave_event_space(...)
?
首先,回想一下我们的最终目标是拥有一个 2 元组 (per_chat_id(), create_open(MessageCounter, ...))
。对于 "patch",它通常意味着 (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
,并且 returns 是一个类似于 pair
的函数(签名与 pair
相同),但具有更复杂的播种器和更多参数标记上。这就是我所说的 "patching".
pave_event_space
是最常见的 "patcher"。其他修补程序包括 include_callback_query_chat_id
和 intercept_callback_query_origin
。它们基本上都做同样的事情:接受一个类似 pair
的函数,returns 另一个类似 pair
的函数,带有更复杂的播种器和更多标记的参数。因为输入和输出相似,所以可以将它们链接起来应用多个补丁。如果你查看 callback examples,你会看到类似这样的内容:
bot = DelegatorBot(TOKEN, [
include_callback_query_chat_id(
pave_event_space())(
per_chat_id(), create_open, Lover, timeout=10),
])
它修补事件 space 的东西,然后修补回调查询的东西,使播种器 (per_chat_id()
) 和处理程序 (Lover
) 能够协同工作。
我现在只能说这些了。我希望这对代码有所启发。祝你好运。