如何动态修改函数的本地命名空间?

时间:2012-05-07 19:59:04

标签: python namespaces locals

注意:这个问题假设是Python 2.7.3。

我正在寻找一种理智的方法来动态修改函数的本地命名空间,最好是以一种给body函数添加最少杂乱的方式。

我的想法会是这样的:

import os
from namespace_updater import update_locals

def somefunc(x, y, z):
    # ...
    # ...
    # this and that
    # ...
    # ...

    if os.environ.get('FROBNICATE'):
        from frobnitz import frobnicate
        update_locals(frobnicate(locals()))

    #
    # life goes on, possibly with duly frobnicated local variables...
    # ...
    # ...
    # ...

谢谢!


PS:以下是不起作用的方法。

最天真的方法是:

locals().update(new_locals(locals())

...但是documentation for locals()非常明确地警告不要依赖这样的伏都教来修改局部变量,所以请不要将其作为答案提交(除非您能够做出无视文档警告的优秀案例)。

接下来的天真尺度就像是

for k, v in new_locals(locals()).items():
    exec ('%s = v' % k)

AFAICT,这样的代码不能“不在路上”(即 在函数体内),这是不理想的。但真正的交易破坏是exec ('%s = v' % k)黑客可以导致一些奇怪的错误。

当我写出“奇怪的错误”时,我的意思是“对于那些像我一样对exec ('%s = v' % k)的把握很脆弱的人看起来很奇怪的错误”。我对这个黑客的把握有多么脆弱?要回答这个问题,请考虑以下脚本。它有三种变体:(1)完全如图所示; (2)删除第18行的前导#后; (3)删除第15和18行中的第一个#后(即对于此变体,没有注释掉代码)。我无法预测此脚本的变体(2)和(3)的行为。我甚至无法预测变体(1)的行为超过50%。这是我对exec ('%s = v' % k)黑客的理解。除非你能够自信而正确地预测这个脚本的三个变体将如何表现(在python 2.7下),可以肯定地说你对情况的掌握与我的一样脆弱,你可能应该清楚{{1也是。

exec ('%s = v' % k)

3 个答案:

答案 0 :(得分:1)

我将提出我认为接近合理的唯一方法,然后我会试着说服你不要使用它。

def process(**kw):
  mycode = """\
print 'Value of foo is %s' % (foo,)
print 'Value of bar is %s' % (bar,)
"""
  exec mycode in kw

vars = {'foo': 2, 'bar': 3}
process(**vars)

使用这种方法,您至少可以获得一些代码注入攻击保护。包含代码“局部变量”的字典是明确指定的,因此您可以完全控制运行exec语句时变量空间的内容。您不必破解函数对象或其他类似物的内部。

我知道decorator moduleexec的实现中使用@decorator来操纵动态创建函数中的参数名称,并且可能还有其他常用模块使用它。但我只有一种情况,exec明确胜过Python的替代品,而eval则取胜。{/ p>

我在你的问题中没有看到这种情况。除非上面的mycode需要做一些非常时髦的事情,比如在kw中创建一个带有参数名称的函数,否则很可能只是简单地编写代码,并且可能使用{{1}在紧要关头。

locals()

答案 1 :(得分:0)

可能是那样的

def foo():
    print(x)

foo.__globals__["x"] = "Hello Python"

foo()

不幸的是,如果定义了varible,这在函数体中不起作用

def foo(flag):
    x = "Hello World"
    if flag:
        foo.__globals__["x"] = "Hello Python"

    print(x)

标志中打印 Hello World 为True或False

答案 2 :(得分:0)

不确定是否只能使用外部功能。我创建了一个片段:

def get_module_prefix(mod, localsDict):
    for name, value in localsDict.iteritems():
        if value == mod:
            return name
    raise Exception("Not found")

def get_new_locals(mod, localsDict):
    modulePrefix = get_module_prefix(mod, localsDict)
    stmts = []
    for name in dir(mod):
        if name.startswith('_'):
            continue
        if name not in localsDict:
            continue
        stmts.append("%s = %s.%s" % (name, modulePrefix, name))
    return "\n".join(stmts)

def func(someName):
    from some.dotted.prefix import some.dotted.name
    #here we update locals
    exec(get_new_locals(some.dotted.name, "some.dotted.name", locals()))
    print locals()
    print someName # value taken from aModule instead of parameter value


func(5)

其中:

  • get_module_prefix用于查找导入模块的名称,
  • get_new_locals返回可用于更新本地人的赋值语句,

本地的实际更新是在行exec(get_new_locals(some.dotted.name, locals()))中执行的,我们只需执行赋值语句,我们将模块中的值赋给局部变量。

我不确定这是不是你的确实。