Почему я не могу перебрать locals () и внутри итерации использовать возвращаемый элемент в качестве ключа? - PullRequest
2 голосов
/ 18 апреля 2020

У меня есть модуль Promotions с некоторыми функциями, и у меня есть переменная, в которой хранится список всех функций, содержащих в имени «_promo».

promos = [
    locals()[name] for name in locals() # Iterate over each name in the dictionary returned by locals()
    if name.endswith("_promo") # Select only names that end with the _promo suffix.
    ] 

Когда я импортирую Акции в другое место и прошу получить промо, я получаю KeyError.

Однако, если я делаю то же самое, но заменяю locals() на globals(), я не получаю KeyError.

Кто-нибудь знает почему?

edit: Это потому, что во второй раз я звоню locals() ( в locals()[name]) я больше не в той же сфере?

1 Ответ

2 голосов
/ 18 апреля 2020

Это потому, что во второй раз, когда я вызываю 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")]
...