【问题标题】:Maybe monad in Python with method chaining也许 Python 中的 monad 带有方法链
【发布时间】:2015-04-20 20:58:24
【问题描述】:

我正在尝试在 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)

我有两个功能:

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)

这是我的使用方法:

 result = Maybe(5).do(double).do(square)
    print(result.val)

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

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

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

这个可以改进吗?

非常感谢。

【问题讨论】:

  • 这在代码审查方面会更好,因为您不是在尝试解决特定问题,而只是想在代码中添加一些 cmets。
  • 不,这不是更好,因为代码也不起作用:D
  • 我认为这是上下文:Maybe monad
  • Maybe(0).do(double)

标签: python exception-handling monads method-chaining maybe


【解决方案1】:

这样做的缺点是它故意抑制错误,这在 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

这会产生:

26
TypeError("can't multiply sequence by non-int of type 'str'",)

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

无论你如何分割它,这个习语都会让人觉得有点“不合Python”,但这是一种更健壮的处理方式。

【讨论】:

  • 谢谢乔……这看起来更好……但你为什么称这种方法为“unpythonic”?
  • 主要是错误不会“传播”。相反,您需要检查返回的值。代替 try/except 的值检查通常被认为是非 Python 的。这不一定是坏事,只是看起来总是有点格格不入。这基本上是“三思而后行”与“请求宽恕比许可更容易”的古老论点。 Python 更喜欢后者。
【解决方案2】:

以下内容可以满足您的要求,并让函数更加简洁。还更新以正确捕获异常:

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)

打印:

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

【讨论】:

  • 谢谢......个别方法中的异常处理真的让我很烦。这看起来好多了。
【解决方案3】:

这个问题已经被回答了,但是我觉得使用 Maybe monad 的界面可以变得更具可读性。允许左右调用的接口,例如:

 square(double(Maybe(5)))

另外,最好返回一个 Maybe 类型给用户使用,这样他们就可以选择进一步链接而不是立即检索该值。这个功能类似于 Swift 的 Optional 类型。

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

  def unwrap(self):
     return self.val

  def calc(self, func):
     try:
       return func(self.val)
     except:
       return None

def double(number):
    return Maybe(maybe.calc(lambda x: x*2))

def square(number):
    return Maybe(maybe.calc(lambda x: x**2))

print square(double(Maybe(5))) # prints Maybe type
print square(double(Maybe(5))).unwrap() # prints '100'
print square(double(None)) # prints 'None'

异常不会存储在我的答案中,因为调用者希望 maybe monad 检查异常本身并在链接操作失败时转换为“无”(即无)。这允许用户使用链轻松处理条件中的故障。

【讨论】:

    【解决方案4】:

    我可能会考虑一些调整:

    • 按照其他人的建议存储异常。
    • 使Maybe 可调用以消除对do 的需要。
    • 通过调用传递xskws,以防您的任何函数采用其他参数。

    例如:

    class Maybe:
        def __init__(self, val, error = None):
            self.val = val
            self.error = error
    
        def __call__(self, func, *xs, **kws):
            v = None
            e = None
            if self.val is not None:
                try:
                    v = func(self.val, *xs, **kws)
                except Exception as e:
                    pass
            return Maybe(v, error = e)
    
    def double(n):
        return n * 2
    
    def square(n):
        return n * n
    
    def multiply_by(n, by):
        return n * by
    
    def message(n, *xs, **kws):
        return 'Message: {} {} {}'.format(n, xs, kws)
    
    ms = [
        Maybe(5)(double)(square),
        Maybe(4)(square)(square)(double),
        Maybe(3)(square)(square),
        Maybe(7)(multiply_by, 3),
        Maybe(9)(message, 1, 2, 3, foo = 'blah'),
        Maybe('hi')(double)(square),
    ]
    
    for m in ms:
        print(dict(val = m.val, error = m.error))
    

    【讨论】:

    • 非常感谢 .. 我现在被宠坏了!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-03
    • 1970-01-01
    • 2018-08-07
    • 2012-04-28
    • 2020-11-12
    相关资源
    最近更新 更多