python - Python 如何使用装饰器实现有限状态机?

 

问题描述:

我有一个需求,需要类似有限状态机的功能,比如一个工作流程,有 4 个步骤,没个步骤都可能引发某种异常,或者返回某个值,这导致下一步需要执行的步骤存在多种可能,常规的写法是一大坨 if-else,另外,步骤之间可能需要回滚重试,这使得 if-else 也不太好写。
我构想了一种方式,灵感来自有限状态机,初步的期望用法如下,但是我不知道要如何去实现?

@fsm(condition=Exception, method='step2')
@fsm(condition=True, method='step3')
@fsm(condition=False, method='step4')
def step1(arg):
    return arg


@fsm(condition=Exception, method='step2', retry=3)
@fsm(condition=True, method='step3')
@fsm(condition=False, method='step4')
def step2(arg):
    return arg


@fsm(condition=Exception, method='step1', retry=3)
@fsm(condition=True, method='step3')
@fsm(condition=False, method='step4')
def step3(arg):
    return arg


@fsm(condition=Exception, retry=3)
def step4(arg):
    return arg

# 启动流程。
step1('arg')

 

第 1 个答案:

推测一下你想要实现的功能:

  1. 转发。实现一个函数装饰器,参数两个:条件值(condition)和后续动作函数(method),当当前函数运行结束并返回的时,判断返回值是否与条件值匹配,如果匹配,调用后续动作函数。
  2. 重试。实现一个函数装饰圈,参数两个:重试次数(retry)和全部失败后的后续动作函数(method)。如果当前函数运行失败,且失败次数小于重试次数,用原参数重试当前函数;如果超过重试次数,调用后续动作函数收尾。

关于转发和重试时,后续动作函数的参数 arg,从你的描述中不太明白该取何值,将原 arg 传递到下游。代码实现如下:

def fsmError(retry, method):
    def newFunc(f):
        def g(args):
            try:
                return f(args)
            except:
                if retry > 0:
                    return fsmError(retry - 1, method)(f)(args)
                else:
                    d = globals()[method]
                    return d(args)

        return g

    return newFunc


def fsm(condition, method):
    def newFunc(f):
        def g(args):
            res = f(args)
            if condition == res:
                n = globals()[method]
                return n(args)
            return res

        return g

    return newFunc


mockRetry = 0


@fsmError(retry=5, method="error")
@fsm(condition=True, method="step2")
@fsm(condition=False, method="step3")
def step1(args):
    global mockRetry
    mockRetry += 1
    print("第" + str(mockRetry) + "次调用")

    if mockRetry < 3:
        1 / 0

    print("step1")
    return True


@fsm(condition=None, method="step3")
def step2(args):
    print("step2")


def step3(args):
    print("step3")


def error(args):
    print("重试失败")


if __name__ == "__main__":
    res = step1(0)

amh 大大,2023年新年好,我遇到几个问题 一、当开启了默认站点的时候 无法对站点的443添加默认 仅对80端口有效,也是为了提高服务器的安全性参考的是这个:https://hqidi.com/195.html ,目 ...