也许Python中的monad与方法链接

laz*_*hon 6 python monads exception-handling method-chaining maybe

我正在尝试在python中实现Maybe monad.然而,我也想要的是某种链接能力.

所以我有一节课:

class Maybe:
    def __init__(self, val):
        self.val = val

    def do(self, func):  # Bind function
        if self.val is None:
            return None
        else:
            return func(self.val)
Run Code Online (Sandbox Code Playgroud)

我有两个功能:

def double(number):
    try:
        result = number * 2
        return Maybe(result)
    except:
        return Maybe(None)

def square(number):
    try:
        result = number * number
        return Maybe(result)
    except:
        return Maybe(None)
Run Code Online (Sandbox Code Playgroud)

这是我如何使用它:

 result = Maybe(5).do(double).do(square)
    print(result.val)
Run Code Online (Sandbox Code Playgroud)

我正在寻找一种链接多个函数的方法,每个函数执行一个特定的任务.每个函数都将前一个函数的输出作为输入.如果链中的任何函数抛出异常,链应该断开.

这是对Maybe monad进行建模的正确方法吗?

这是处理异常的正确方法吗?

这可以改进吗?

非常感谢.

Joe*_*ton 7

这样做的缺点是它故意抑制错误,这在 python 中通常被认为是一个坏主意。

但是,您可以捕获并存储Maybe实例中发生的任何错误并将其报告回来。

例如:

class Maybe(object):
    def __init__(self, val, error=None):
        self.val = val
        self.error = error

    def __repr__(self):
        if self.val is not None:
            return repr(self.val)
        else:
            return repr(self.error)

    def do(self, func):
        if self.val is None:
            return self
        try:
            return Maybe(func(self.val))
        except Exception as e:
            return Maybe(None, e)

def squared(x):
    return x * x

def addone(x):
    return x + 1

result1 = Maybe(5).do(squared).do(addone)
result2 = Maybe('a').do(squared).do(addone)
print result1
print result2
Run Code Online (Sandbox Code Playgroud)

这产生:

26
TypeError("can't multiply sequence by non-int of type 'str'",)
Run Code Online (Sandbox Code Playgroud)

这类似于 DanD 的答案,但具有存储发生的错误而不是完全抑制它的优点。

无论你如何切片,这个习语都会让人感觉有点“非pythonic”,但这是一种稍微更健壮的处理方式。


Dan*_* D. 5

以下内容可以满足您的要求,并使功能更简洁一些。还进行了更新以正确捕获异常:

class Maybe:
    def __init__(self, val, err=None):
        self.val = val
        self.err = err

    def __repr__(self):
        if self.err is not None:
           return 'Maybe('+repr(self.val)+', '+repr(self.err)+')'
        else:
           return 'Maybe('+repr(self.val)+')'

    def do(self, func):  # Bind function
        if self.val is not None:
            try:
                val = func(self.val)
            except Exception as e:
                return Maybe(None, e)
            if not isinstance(val, Maybe):
                return Maybe(val)
            else:
                 return val
        else:
            return Maybe(None, self.err)


def double(number):
    result = number * 2
    return result

def square(number):
    result = number * number
    return result

result = Maybe(5).do(double).do(square)
print(result.val)
print(result)
result2 = Maybe('a').do(double).do(square)
print(result2.val)
print(result2)
Run Code Online (Sandbox Code Playgroud)

印刷品:

100
Maybe(100)
None
Maybe(None, TypeError("can't multiply sequence by non-int of type 'str'",))
Run Code Online (Sandbox Code Playgroud)