将WeakValueDictionary命名空间传递给exec

时间:2017-01-19 16:44:33

标签: python python-3.x

我希望更好地理解我使用exec函数观察到的行为。

exec函数不允许对名称空间映射参数(globalslocals)使用非dict类型。例如:

from weakref import WeakValueDictionary as wdict

# dummy object to put in the wdict since wdict can't hold references to 
# most built-in types:
a = type('dummy', (), {})() 
exec('print(a)', wdict(a = a)) # TypeError: exec() globals must be a dict, not wdict

那不是问题;我们通过创建一个也是dict的弱词典来解决这个问题:

mydict = type('mydict', (wdict, dict), {})

m = mydict(a = a)
list(m) # ['a'] <-- entry for dummy() object

mydict按预期工作......

del a
import gc
gc.collect() # force garbage collection and mydict/wdict update
list(m) # [] <--- entry for the dummy() object removed as expected

所以我们现在可以传递mydict作为exec的全局命名空间:

a = type('dummy', (), {})()
m = mydict(a = a)
exec('print(a)', m) # dummy object printed as expected

鉴于上述情况,似乎任何添加到mydict命名空间的新对象 - 即在我们的代码块中创建的全局内容 - 将是短暂的(仅创建弱引用,因此需要进行垃圾回收):< / p>

exec('test = type("test", (), {})', m)
gc.collect()
test = m['test'] # KeyError: 'test'

这可能是一个有用的情况。例如,假设我想创建一个交互式代码会话,其中用户无法在提供的命名空间中创建任何永久对象(例如,向学生管理测试),但他们可以更新/更改现有对象:

code = sanitized_input_file('somefile.py')
examine_code(code)
namespace = mydict(a = InterestingObject1(), b = InterestingObject2())
exec(code, namespace)
gc.collect()
examine_namespace(namespace)

当然这有些限制,因为用户无法为许多内置对象添加临时引用,例如intmydict。但是,这可以通过将我自己的一组内置函数提供给globals命名空间作为本机的精简包装来解决。

另外值得注意的是:像往常一样,我们仍然可以使用evalcompile捕获表达式:

test = eval('type("test", (), dict(a = a)))', m) 

...并保留在命名空间中创建的任何具体引用:

del a
gc.collect()
print(test.a) # dummy object reference still exists

我的问题是:我对这种行为的理解 - 对象的短暂性质被添加到我的弱控制命名空间 - 正确吗?我可以确定命名空间中新创建的对象不会持久存在吗?如果我要尝试利用它来完成有趣的事情,我应该注意哪些陷阱?

0 个答案:

没有答案