-2

当我为 传递真正的 dict 时localsexec()会做正确的事情并回退到 globals 字典以查找缺少的名称。但是,如果我将 LazyMap(类 dict 对象)作为局部变量传递,则对全局变量的访问会从提供给 LazyMap 的 lambda 内部引发 AttributeError。

#!/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)

为什么 LazyMap 会引发异常,但普通 dict 不会引发异常?如果从 exec 访问,我如何创建一个只会获取/计算值的本地映射?

4

1 回答 1

0

我猜想 exec() 首先尝试 getitem 在 locals 对象上,如果 aKeyError被提升,它然后回退到 getitem 上 globals 对象。但是,在给定的代码中,getattr将引发 AttributeError 而不是 KeyError。将传递的 lambda 更改为vars(s)[k]将导致缺少的键改为引发正确的 KeyError。

总结一下:__getitem__应该KeyError在缺少键时加注,而__getattr__应该加注AttributeError

于 2017-01-10T04:46:07.327 回答