python:改变`globals`以动态地将事物放入范围

时间:2012-07-21 16:53:06

标签: python monads

这是一个多么可怕的想法? class monad实现了with接口以将事物放入和放出作用域,因此我可以编写一个通用函数库,如m_chain,它们引用函数unitbind谁可以在运行时放入实现。 (所有这些代码的作用或者它是一个好主意并不重要。)

我试过的其他想法都围绕着传递一个包含unit / bind作为参数或kwarg的结构,或者将m_chain放在一个类中,用self.unit和self.bind来实现它,并让派生类提供它们。但它增加了代码和语法的复杂性,并将单元/绑定与monad在python中表达的方式联系起来。使用范围只是感觉好多了。

class monad:
    """Effectively, put the monad definition in lexical scope.
    Can't modify the execution environment `globals()` directly, because
    after globals().clear() you can't do anything.
    """
    def __init__(self, monad):
        self.monad = monad
        self.oldglobals = {}

    def __enter__(self):
        for k in self.monad:
            if k in globals(): self.oldglobals[k]=globals()[k]
            globals()[k]=self.monad[k]

    def __exit__(self, type, value, traceback):
        """careful to distinguish between None and undefined.
        remove the values we added, then restore the old value only
        if it ever existed"""
        for k in self.monad: del globals()[k]
        for k in self.oldglobals: globals()[k]=self.oldglobals[k]


def m_chain(*fns):
    """returns a function of one argument which performs the monadic
    composition of fns"""
    def m_chain_link(chain_expr, step):
        return lambda v: bind(chain_expr(v), step)
    return reduce(m_chain_link, fns, unit)




identity_m = {
    'bind':lambda v,f:f(v),
    'unit':lambda v:v
}

with monad(identity_m):
    assert m_chain(lambda x:2*x, lambda x:2*x)(2) == 8


maybe_m = {
    'bind':lambda v,f:f(v) if v else None,
    'unit':lambda v:v
}

with monad(maybe_m):
    assert m_chain(lambda x:2*x, lambda x:2*x)(2) == 8
    assert m_chain(lambda x:None, lambda x:2*x)(2) == None

1 个答案:

答案 0 :(得分:0)

我认为连续打击全球化绝对是一个糟糕的主意。依赖全局变量看起来就像你在这里模仿的功能风格的对立面。

为什么不将m_chain定义为:

def m_chain(bind, *fns):
    """returns a function of one argument which performs the monadic
    composition of fns"""
    def m_chain_link(chain_expr, step):
        return lambda v: bind(chain_expr(v), step)
    return reduce(m_chain_link, fns, unit)

然后:

identity_m = {
    'bind':lambda v,f:f(v),
    'unit':lambda v:v
}

with monad(identity_m):
    assert m_chain(lambda x:2*x, lambda x:2*x)(2) == 8

变得简单:

assert m_chain(lambda v,f:f(v), lambda x:2*x, lambda x:2*x)(2) == 8

实际上明确地传递函数似乎更加pythonic并且似乎不会导致您失去任何灵活性。