我有一个函数m_chain,它引用了两个未定义的函数bind
和unit
。我想在为这些函数提供定义的某些上下文中包装此函数 - 您可以将它们视为我想为其动态提供实现的接口(interface)。
def m_chain(*fns):
"""what this function does is not relevant to the question"""
def m_chain_link(chain_expr, step):
return lambda v: bind(chain_expr(v), step)
return reduce(m_chain_link, fns, unit)
在 Clojure 中,这是通过宏完成的。在 python 中有哪些优雅的方法可以做到这一点?我考虑过:
- 多态性:将 m_chain 转化为引用
self.bind
和self.unit
的方法,其实现由子类提供 - 实现
with
接口(interface),这样我就可以修改环境映射,然后在完成后进行清理 - 更改 m_chain 的签名以接受单元并绑定(bind)为参数
- 要求 m_chain 的使用被装饰器包装,装饰器会做某事或其他事情 - 不确定这是否有意义
理想情况下,我根本不想修改 m_chain,我想按原样使用定义,而以上所有选项都需要更改定义。这一点很重要,因为还有其他 m_* 函数引用在运行时提供的附加函数。
我如何最好地构造它以便我可以很好地传递绑定(bind)和单元的实现?重要的是 m_chain 的最终用法要真正易于使用,尽管实现很复杂。
编辑:这是另一种可行的方法,它非常丑陋,因为它需要将 m_chain 柯里化(Currying)为一个没有参数的函数。但这是一个最低限度的工作示例。
def domonad(monad, cmf):
bind = monad['bind']; unit = monad['unit']
return cmf()
identity_m = {
'bind':lambda v,f:f(v),
'unit':lambda v:v
}
maybe_m = {
'bind':lambda v,f:f(v) if v else None,
'unit':lambda v:v
}
>>> domonad(identity_m, lambda: m_chain(lambda x: 2*x, lambda x:2*x)(2))
8
>>> domonad(maybe_m, lambda: m_chain(lambda x: None, lambda x:2*x)(2))
None
最佳答案
在 Python 中,您可以编写所有您想要引用不存在的东西的代码;具体来说,您可以编写引用没有绑定(bind)值的名称的代码。您可以编译该代码。唯一的问题将发生在运行时,如果名称仍然没有绑定(bind)到它们的值。
这是您可以在 Python 2 和 Python 3 下测试并运行的代码示例。
def my_func(a, b):
return foo(a) + bar(b)
try:
my_func(1, 2)
except NameError:
print("didn't work") # name "foo" not bound
# bind name "foo" as a function
def foo(a):
return a**2
# bind name "bar" as a function
def bar(b):
return b * 3
print(my_func(1, 2)) # prints 7
如果您不希望名称仅绑定(bind)在本地 namespace 中,但您希望能够对每个函数进行微调,我认为 Python 中的最佳实践是使用命名参数。你总是可以像这样关闭函数参数并返回一个新的函数对象:
def my_func_factory(foo, bar):
def my_func(a, b):
return foo(a) + bar(b)
return my_func
my_func0 = my_func_factory(lambda x: 2*x, lambda x:2*x)
print(my_func0(1, 2)) # prints 6
编辑:这是您的示例,使用上述想法进行了修改。
def domonad(monad, *cmf):
def m_chain(fns, bind=monad['bind'], unit=monad['unit']):
"""what this function does is not relevant to the question"""
def m_chain_link(chain_expr, step):
return lambda v: bind(chain_expr(v), step)
return reduce(m_chain_link, fns, unit)
return m_chain(cmf)
identity_m = {
'bind':lambda v,f:f(v),
'unit':lambda v:v
}
maybe_m = {
'bind':lambda v,f:f(v) if v else None,
'unit':lambda v:v
}
print(domonad(identity_m, lambda x: 2*x, lambda x:2*x)(2)) # prints 8
print(domonad(maybe_m, lambda x: None, lambda x:2*x)(2)) # prints None
请告诉我这对您有何帮助。
编辑:好的,在您的评论之后再发布一个版本。您可以按照此模式编写任意 m_
函数:它们检查 kwargs
中的键 “monad”
。这必须设置为命名参数;没有办法将它作为位置参数传递,因为 *fns
参数将所有参数收集到一个列表中。我为 bind()
和 unit()
提供了默认值,以防它们没有在 monad 中定义,或者没有提供 monad;这些可能无法满足您的要求,因此请用更好的东西替换它们。
def m_chain(*fns, **kwargs):
"""what this function does is not relevant to the question"""
def bind(v, f): # default bind if not in monad
return f(v),
def unit(v): # default unit if not in monad
return v
if "monad" in kwargs:
monad = kwargs["monad"]
bind = monad.get("bind", bind)
unit = monad.get("unit", unit)
def m_chain_link(chain_expr, step):
return lambda v: bind(chain_expr(v), step)
return reduce(m_chain_link, fns, unit)
def domonad(fn, *fns, **kwargs):
return fn(*fns, **kwargs)
identity_m = {
'bind':lambda v,f:f(v),
'unit':lambda v:v
}
maybe_m = {
'bind':lambda v,f:f(v) if v else None,
'unit':lambda v:v
}
print(domonad(m_chain, lambda x: 2*x, lambda x:2*x, monad=identity_m)(2))
print(domonad(m_chain, lambda x: None, lambda x:2*x, monad=maybe_m)(2))
关于python 后期绑定(bind) - 动态地将局部变量放在范围内,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11588289/