Неожиданное поведение генератора, вложенного в понимание

#python #python-3.x #python-2.7 #generator #dictionary-comprehension

#python #python-3.x #python-2.7 #генератор #словарь-понимание

Вопрос:

Я получаю неожиданное поведение в Python при использовании генератора, вложенного в понимание, понимание словаря в данном конкретном случае ниже. Более конкретно, рассмотрим следующее простое понимание:

 D = {x : (y for y in range(5) if y==x) for x in range(5)}
 

Я ожидал бы получить такой словарь D , что list(D[x]) == [x] для каждого целого числа i от 0 до 4. Вместо этого результат

 >>> list(D[0])
[4]
>>> list(D[1])
[4]
>>> list(D[2])
[4]
>>> list(D[3])
[4]
>>> list(D[4])
[4]
 

Я думаю, что это происходит потому, что значение переменной x , используемой для определения генератора, меняется до тех пор, пока не будет зафиксировано на 4. Однако я подумал, что каждый генератор должен определяться с точным значением переменной во время определения. Есть ли альтернативный способ исправить эту простую вложенную структуру исправленным способом и с ожидаемыми результатами?

Обратите внимание, что преобразование генератора в список исправит поведение:

 D = {x : list((y for y in range(5) if y==x)) for x in range(5)}
 

но меня интересуют решения, которые сохраняют использование генераторов.

Комментарии:

1. Каждый генератор будет выдавать только один элемент; зачем беспокоиться? Или вы думаете, что один генератор будет использоваться всеми 5 ключами? Это не так, как dict работает.

2. x это просто свободная переменная внутри генератора; ее значение не будет просматриваться до тех пор, пока вы фактически не выполните итерацию по генератору.

3. Непонятно, зачем вам нужно внутреннее понимание? lambda Вместо этого используйте a!

4. Этот пример — всего лишь игрушечный пример, позволяющий легко исследовать это неожиданное поведение, вдохновленное более значимым случаем

Ответ №1:

x является свободной переменной, поэтому ее значение не будет учитываться до тех пор, пока вы фактически не выполните итерацию по генератору. Это, как вы заметили, означает, что значение x может меняться в промежутке между определением генератора и его фактическим использованием.

Невозможно передать значение в выражение генератора; вам придется использовать функцию генератора:

 def make_generator(x):
    for y in range(5):
        if y == x:
            yield y

D = {x: make_generator(x) for x in range(5)}
 

Ответ №2:

Для сценария, который вы описали (я не понимаю, зачем вам нужно внутреннее понимание), вы можете просто использовать следующее:

 d = {x:lambda x: [x] for x in range(5)}
print(d[3](3))
 

который даст:

 [3]