我希望更好地理解我使用exec
函数观察到的行为。
exec
函数不允许对名称空间映射参数(globals
,locals
)使用非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)
当然这有些限制,因为用户无法为许多内置对象添加临时引用,例如int
,mydict
。但是,这可以通过将我自己的一组内置函数提供给globals命名空间作为本机的精简包装来解决。
另外值得注意的是:像往常一样,我们仍然可以使用eval
或compile
捕获表达式:
test = eval('type("test", (), dict(a = a)))', m)
...并保留在命名空间中创建的任何具体引用:
del a
gc.collect()
print(test.a) # dummy object reference still exists
我的问题是:我对这种行为的理解 - 对象的短暂性质被添加到我的弱控制命名空间 - 正确吗?我可以确定命名空间中新创建的对象不会持久存在吗?如果我要尝试利用它来完成有趣的事情,我应该注意哪些陷阱?