写点什么

Python 进阶之 Monad

用户头像
正向成长
关注
发布于: 2021 年 01 月 03 日
Python进阶之Monad

假设实现讲一个字符转换为 int,再取反,之后返回一个字符,相关实现如下:

from operator import negy = str(neg(int('1'))) # y = -1
复制代码

如果上述实现传入的参数是'xyz',那么上述实现会抛出异常,如果不进行异常捕获处理,那么就会导致程序崩溃。我们可以考虑,我们是否可以实现这样的一个功能,保存一个标志,判断是否存在异常,如果存在异常则捕获并返回None和设置标志为True。很显然strnegint函数并不具备这样的功能,此时便是 Monad 的用武之地。


在[1]文章中作者指出:

A monad is a design pattern that allows us to add a context to data values, and also allows us to easily compose existing functions so that they execute in a context aware manner.

翻译就是:Monad 是一种设计模式,它使得我们可以向数据中添加上下文,进而实现一些组合功能,从而达到可以感知上下文的方式运行。

Failure Monad

下面实现上述的 Monad,主要是添加一个 bind 方法主要实现:

  • 从 Monad 中取出value参数

  • 执行bind方法的入参(一个函数)

  • 包装一个新的 Monad 并返回

class Failure:    def __init__(self, value, failed = False):        self.value = value        self.failed = failed        def get(self):        return self.value        def __str__(self):        return ' '.join([str(self.value), str(self.failed)])        def bind(self, f):        if self.failed:            return self        try:            x = f(self.value)            return Failure(x)        except:            return Failure(None, True)
# 测试数据print(Failure(1).bind(int).bind(neg).bind(str)) # -1 Falseprint(Failure("xyz").bind(int).bind(neg).bind(str)) # None True
复制代码

接下来我们进行一些语法改进,通过重载|运算符,达到 bind 函数的实现。

    def __or__(self, f):        return self.bind(f)
复制代码

那么,上述测试案例可以按照如下方式实现:

print(Failure('1') | int | neg | str) # -1 Falseprint(Failure('xyz') | int | neg | str) # None True
复制代码

反观,这种实现的结果和 Linux 的管道,形式上类似。


List Monad

List Monad 主要实现以下功能:

  • 实现对一个 List Monad 中的 value 进行封装

  • 通过 bind 函数实现将一个函数 list 所有元素处理

class List:    def __init__(self, value):        self.value = value        def get(self):        return self.value        def __str__(self):        return ' '.join(map(str, self.value))        def bind(self, f):        y = map(f, self.value)        return List(y)        def __or__(self, f):        return self.bind(f)
# 测试程序print(List([1,2,3]).bind(neg)) # -1 -2 -3print(List([1,2,3]) | neg) # -1 -2 -3print(List([1, 2, 3]).bind(str).bind(lambda s: s.zfill(4))) # 0001 0002 0003
复制代码

Maybe monad

在实际程序运行中,还经常出现读取到不存在值的情况,例如,查找一个不存在的值,或在尝试从数据库中读取一条根本就不存在的记录。解决这种情况常见的实现方式是将变量置为 None,但是这存在一些问题:

  • 程序中需要一系列if not x的语句,对值不存在的情况进行处理

  • 有时 None 并不代表值不存在,而是表示计算中遇到了错误,或者其他情况。

  • 有时 None 是该字段一个有效状态,且不表示特殊意义,例如不存在的状态等。

基于此,提供 Just Monad 和 Nothing Monad 分别表示值存在和不存在两种情况。

Just Monad

class Just():    def __init__(self, value):        self.value = value        def get(self):        return self.value        def bind(self, f):        result = f(self.value)        return Just(result)        def __str__(self):        return 'Just(' + str(self.value) + ')'        def __or__(self, f):        return self.bind(f)
# 测试程序print(Just(3) | neg) # Just(-3)
复制代码


Nothing Monad

此时,无value,所以bind函数的返回值总是Nothing()

class Nothing():    def __init__(self):        pass        def get(self):        return None        def bind(self, f):        return Nothing()        def __str__(self):        return 'Nothing()'        def __or__(self, f):        return self.bind(f)
# 测试程序print(Nothing().bind(neg))# Nothing()
复制代码


参考资料

  1. Monads in Python

  2. More Monads in Python


发布于: 2021 年 01 月 03 日阅读数: 52
用户头像

正向成长

关注

正向成长 2018.08.06 加入

想要坚定地做大规模数据处理(流数据方向),希望结合结合批处理的传统处理方式,以及之后流批混合处理方向进行学习和记录。

评论

发布
暂无评论
Python进阶之Monad