When I pass a bona fide dict for locals
, exec()
does the correct thing and falls back to the globals dictionary for missing names. However, if I pass a LazyMap (dict-like object) as locals, accesses to globals raise an AttributeError from inside the lambda given to LazyMap.
#!/usr/bin/env python3
class LazyMap:
def __init__(self, keys, getter):
self._keys = keys
self._getter = getter
def keys(self):
return self._keys
def __getitem__(self, k):
return self._getter(k)
class TestObj:
def __init__(self):
self.field = 'foo'
obj = TestObj()
# this prints 'foo'
locs = dict((name, getattr(obj, name)) for name in dir(obj))
exec('print(field)', globals(), locs)
# this raises AttributeError
locs = LazyMap(dir(obj), lambda k,s=obj: getattr(s, k))
exec('print(field)', globals(), locs)
Why is an exception raised with LazyMap but not with a plain dict? How can I create a locals map that will only fetch/compute the value if accessed from the exec?