4

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

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

我的想法看起来像这样:

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())

...但是文档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) 的行为。我什至无法以超过 50% 的置信度预测变体 (1) 的行为。这就是我对exec ('%s = v' % k)黑客。除非您可以自信且正确地预测该脚本的三个变体的行为方式(在 python 2.7 下),否则可以肯定地说,您对情况的掌握与我的一样脆弱,您可能也应该远离exec ('%s = v' % k)

x = 'global x'                            # 01
y = 'global y'                            # 02
def main():                               # 03
    x = 'local x'                         # 04
    y = 'local y'                         # 05
    run(locals())                         # 06
    print 'OK'                            # 07
    return 0                              # 08
                                          # 09
def run(namespace):                       # 10
    global y                              # 11
    print locals().keys()                 # 12
    for k, v in namespace.items():        # 13
        print '%s <- %r' % (k, v)         # 14
        exec ('%s = v' % k) #in locals()  # 15
    print locals().keys()                 # 16
    x = x                                 # 17
    #z = lambda: k                        # 18
    print x                               # 19
    print y                               # 20
                                          # 21
exit(main())                              # 22
4

3 回答 3

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语句时的变量空间。您不必侵入函数对象或其他类似的内部。

我知道装饰器模块用于在动态创建的函数中操作参数名称exec的实现@decorator,并且可能还有其他常用模块使用它。但我只遇到过一种情况,exec它明显胜过 Python 中的替代方案,另一种情况是eval.

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

def process(**kw):
  print 'Value of foo is %s' % (kw['foo'],)
  print 'Value of bar is %s' % (kw['bar'],)

process(foo=2, bar=3)
于 2012-05-07T21:20:14.603 回答
0

可能是这样的

def foo():
    print(x)

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

foo()

不幸的是,如果已定义变量,这在函数体中不起作用

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

    print(x)

在两个标志中打印Hello World是 True 还是 False

于 2012-05-07T20:19:09.420 回答
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()))在我们简单地执行赋值语句的地方执行的,在这些语句中,我们将模块中的值分配给局部变量。

我不确定这是否是你真正的想法。

于 2012-05-07T20:41:47.883 回答