也许 monad in Python 方法链接
Maybe monad in Python with method chaining
我正在尝试在 python 中实现 Maybe monad。
但是我还想要的是某种链接能力。
所以我有一个 class:
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 建模的正确方法吗?
这也是处理异常的正确方法吗?
这可以改进吗?
非常感谢。
下面的代码完成了您的要求,并让函数更加简洁。还更新以正确捕获异常:
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'",))
这样做的缺点是它有意抑制错误,这在 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 的回答类似,但优点是可以存储发生的错误而不是完全抑制它。
不管怎么切,这个成语都会有点"unpythonic"的感觉,但这是一种稍微稳健的处理方式。
我可能会考虑一些调整:
- 按照其他人的建议存储异常。
- 使
Maybe
可调用以消除对 do
的需要。
- 通过调用传递
xs
和 kws
,以防您的任何函数采用其他参数。
例如:
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))
这个问题已经得到解答,但我觉得使用 Maybe monad 的界面可以更具可读性。允许左右调用的接口,例如:
square(double(Maybe(5)))
此外,最好 return 一个 Maybe 类型供用户使用,这样他们就可以选择进一步链接而不是立即检索值。此功能类似于 Swift 的可选类型。
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 自行检查异常并在链式操作失败时转换为 "nothing"(即 None)。这允许用户在使用链的条件中轻松处理失败。
我正在尝试在 python 中实现 Maybe monad。 但是我还想要的是某种链接能力。
所以我有一个 class:
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 建模的正确方法吗?
这也是处理异常的正确方法吗?
这可以改进吗?
非常感谢。
下面的代码完成了您的要求,并让函数更加简洁。还更新以正确捕获异常:
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'",))
这样做的缺点是它有意抑制错误,这在 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 的回答类似,但优点是可以存储发生的错误而不是完全抑制它。
不管怎么切,这个成语都会有点"unpythonic"的感觉,但这是一种稍微稳健的处理方式。
我可能会考虑一些调整:
- 按照其他人的建议存储异常。
- 使
Maybe
可调用以消除对do
的需要。 - 通过调用传递
xs
和kws
,以防您的任何函数采用其他参数。
例如:
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))
这个问题已经得到解答,但我觉得使用 Maybe monad 的界面可以更具可读性。允许左右调用的接口,例如:
square(double(Maybe(5)))
此外,最好 return 一个 Maybe 类型供用户使用,这样他们就可以选择进一步链接而不是立即检索值。此功能类似于 Swift 的可选类型。
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 自行检查异常并在链式操作失败时转换为 "nothing"(即 None)。这允许用户在使用链的条件中轻松处理失败。