我正在寻找一种理智的方法来动态修改函数的本地命名空间,最好是以一种给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)
答案 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 module在exec
的实现中使用@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()))
中执行的,我们只需执行赋值语句,我们将模块中的值赋给局部变量。
我不确定这是不是你的确实。