Почему Python3 exec() вызывает AttributeError, когда переменная находится не в локальных, а в gl

Когда я передаю добросовестный диктат для locals, exec() делает правильную вещь и возвращается к глобальному словарю для пропущенных имен. Однако, если я передаю LazyMap (объект, похожий на dict) в качестве локальных, доступ к глобальным переменным вызовет AttributeError изнутри лямбда-выражения, данного 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)

Почему с LazyMap возникает исключение, а не с простым указанием? Как я могу создать карту местных жителей, которая будет извлекать / вычислять значение только при обращении из exec?

1 ответ

Я думаю, что exec() сначала пытается получить элемент на объекте localals, и если KeyError поднимается, затем возвращается к объекту globals. Однако в данном коде getattr вызовет AttributeError вместо KeyError. Изменение пройденной лямбды на vars(s)[k] приведет к тому, что пропущенный ключ вызовет правильный KeyError.

Подвести итоги: __getitem__ должен поднять KeyError на недостающий ключ, в то время как __getattr__ должен поднять AttributeError,

Другие вопросы по тегам