python: mutating `globals` to dynamically put things in scope

泪湿孤枕 提交于 2019-12-06 07:17:04

问题


how terrible an idea is this? class monad implements the with interface to put things in and out of scope, so i can write a library of generic functions like m_chain who refer to functions unit and bind who can have an implementation put in at runtime. (It doesn't matter what all this code does or if it's a good idea.)

other ideas i tried all revolved around passing around a structure containing unit/bind as an argument or a kwarg, or putting m_chain in a class, implement it in terms of self.unit and self.bind and having derived classes provide them. but it added complexity to the code and syntax and tied unit/bind to the way monads are expressed in python. using scope for this just feels so much nicer.

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:


I think continuously duckpunching globals is definitely a terrible idea. Relying on globals seems like the antithesis of the functional style you're emulating here.

Why not define m_chain as:

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)

Then:

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

Becomes simply:

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

Actually passing the function explicitly seems more pythonic and doesn't seem to cause you to lose any flexibility.



来源:https://stackoverflow.com/questions/11594090/python-mutating-globals-to-dynamically-put-things-in-scope

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!