python 后期绑定(bind) - 动态地将局部变量放在范围内

标签 python functional-programming monads

我有一个函数m_chain,它引用了两个未定义的函数bindunit。我想在为这些函数提供定义的某些上下文中包装此函数 - 您可以将它们视为我想为其动态提供实现的接口(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.bindself.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/

相关文章:

Python正则表达式替换没有按我的预期工作

python - 在基于 sqlalchemy 的 Web 应用程序中,将 db_session 相关语句放在哪里?

functional-programming - SML 和函数式编程的新手。初学者问题

f# - F# 中是否有标准选项工作流?

haskell - 列表的排列 - Haskell

haskell - `IO`的>> =到底如何工作?

python - 在Python中,如何进行字符串替换并检索替换的子字符串?

python - 使用 ipywidget 进行多项选择测试

list - 从 lisp 中的列表中评估函数

haskell - 输入 FP : Tuple Arguments and Curriable Arguments