Это потому, что во второй раз, когда я вызываю localals () (в locals () [name]), я больше не в той же области видимости?
Это именно тот случай. У понимания списка есть своя собственная область видимости, как у функции, но итератор над locals()
создается во внешней области видимости.
import inspect
class LoudIterable:
def __iter__(self):
print(inspect.currentframe())
return iter([1, 2])
x = [print(inspect.currentframe()) for i in LoudIterable()]
# <frame at 0x0000021795BFD4B8, file '', line 5, code __iter__>
# <frame at 0x0000021795CF8AF8, file '', line 8, code <listcomp>>
# <frame at 0x0000021795CF8AF8, file '', line 8, code <listcomp>>
Вы увидите, что каждая итерация имеет один и тот же кадр, но __iter__
был вызван в другом кадре.
И это имеет смысл, когда вы думаете о генераторах.
non_iterable = 2
x = (i for i in non_iterable)
iter
вызывается на итерируемым способом, хотя мы даже не начали итерацию, вы сразу увидите ошибку: TypeError: 'int' object is not iterable
В любом случае, простое исправление таково:
promos = [v for k, v in locals().items() if k.endswith("_promo")]