Skip to content
On this page

代码行为优化 —— 尝试以下click-like的写法

TIP

这一章节是笔者突然热血来潮写的

关于 saya 的介绍 中,我们提到了这样一个现象

python
@channel.use(ListenerSchema(listening_events=[GroupMessage]))
async def ero(app: Ariadne, group: Group, message: MessageChain):
    if message.display == "涩图来":
        ...
    elif message.display == "涩图去":
        ...
1
2
3
4
5
6

为了让这样的写法能够降低一点,Ariadne 出现了很多官方/第三方的 dispatcher 来解决这个问题。 很高兴,我们的用户也的的确确用了这些东西。

但是,在后面的,笔者发现,很多人,其实是没有意识到分开写的精髓。 让我们来好好探究这方面吧

TIP

以下案例改编自真实情节[1]

梦的开始 —— 一个本子下载器

你想做一个本子下载器,然后,你开始思考这个本子下载器该怎么触发

bash
bz rank
bz random [-H24|-D7|-D30]
bz search [-forward] {关键词}
bz download [-forward] {本子号}
1
2
3
4

然后,顺着这个思路,你写出了这样的 dispatcher,这这样一个函数

TIP

可能有同学好奇为什么不用 Commander 而是 Twilight, 还记得吗,这个例子改编自真实情节。

python
@channel.use(
    ListenerSchema(
        listening_events=[GroupMessage],
        inline_dispatchers=[
            Twilight(
                [
                    FullMatch("bz"),
                    UnionMatch("download", "search", "random", "rank")
                    @ "operation",
                    ArgumentMatch("-forward", action="store_true", optional=True)
                    @ "forward_type",
                    UnionMatch("-H24", "-D7", "-D30", optional=True) @ "rank_time",
                    WildcardMatch() @ "content",
                ]
            )
        ]
    )
)
async def bz(
    app: Ariadne,
    group: Group,
    member: Member,
    operation: RegexResult,
    forward_type: ArgResult,
    rank_time: RegexResult,
    content: RegexResult
):
    operation_str = str(operation.result)
    if operation_str == "search":
        ...
    elif operation_str == "rank":
        ...
    elif operation_str == "search":
        ...
    elif operation_str == "download":
        ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

咱们先不聊这个 dispather 太长这个问题(毕竟我们的重点不是这个)

虽然说你用了 Twilight 这类消息链匹配器来使得适配字符串方面你可以剩下很多心。 但是很明显,这样的代码并不是我们想要的,if...elif...elif 这个来来回回的, 跟那 if 涩图来... elif 涩图去... 不就没有任何区别了。

试试 click-like 的方法

Click 是一个利用很少的代码以可组合的方式创造优雅命令行工具接口的 Python 库。 它是高度可配置的,但却有合理默认值的“命令行接口创建工具”。

python
import click

@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name',
              help='The person to greet.')
def hello(count, name):
    """Simple program that greets NAME for a total of COUNT times."""
    for x in range(count):
        click.echo('Hello %s!' % name)

if __name__ == '__main__':
    hello()
1
2
3
4
5
6
7
8
9
10
11
12
13

这是 click 官方的例子,虽然说 Ariadne 现在并不能做到类似的东西,但是, 这种分类的方法值得我们学习下

我们可以将这四个指令分成四个函数(以下方法究极省略)

python
@channel.use(...FullMatch("bz rank"))
async def bz_rank(...):
    ...

@channel.use(...FullMatch("bz search"))
async def bz_search(...):
    ...

@channel.use(...FullMatch("bz random"))
async def bz_random(...):
    ...

@channel.use(...FullMatch("bz download"))
async def bz_download(...):
    ...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

然后下一个问题就出现了,可能函数与函数之间,会有很多相同的代码,怎么办?
那你直接将这些实现包装成一个类不就行了?

python

class Bz:

    def __init__():
        ...

    def encrypt():
        ...

    async def request():
        ...

    async def search():
        ...

    async def download():
        ...

    async def rank():
        ...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Tip:

本文档使用 CC BY-NC-SA 4.0 协议进行共享,详情见 README

MIT License