Объекты-генераторы уникальны, но они ссылаются на i
и j
, но понимание списка прекращается (что, по сути, создает область действия функции, точно так же, как выражения генератора внутри понимания списка). Таким образом, i
и j
имеют значения i == 1
и j == range(4)
. Вы даже можете проанализировать это:
In [1]: l = [(i*2+x for x in j) for i,j in zip([0,1],[range(4),range(4)])]
In [2]: g = l[0]
In [3]: g.gi_frame.f_locals
Out[3]: {'.0': <range_iterator at 0x10e9be960>, 'i': 1}
По сути, это та же самая причина, по которой происходит это часто удивительное поведение:
In [4]: fs = [lambda: i for i in range(3)]
In [5]: fs[0]
Out[5]: <function __main__.<listcomp>.<lambda>()>
In [6]: fs[0]()
Out[6]: 2
In [7]: fs[1]()
Out[7]: 2
In [8]: fs[2]()
Out[8]: 2
Это можно исправить с помощью того же решения, которое заключается в создании другой охватывающей области действия , которая локально связывает переменные с чем-то, что не изменится. Использование функции (здесь лямбда, но это может быть обычная функция) будет прекрасно работать:
In [9]: l = [(lambda i, j: (i*2+x for x in j))(i, j) for i,j in zip([0,1],[range(4),range(4)])]
In [10]: list(l[0])
Out[10]: [0, 1, 2, 3]
In [11]: list(l[1])
Out[11]: [2, 3, 4, 5]
Хотя, возможно, для ясности, я буду использовать разные имена параметров, чтобы сделать более очевидным, что происходит:
In [12]: l = [(lambda a, b: (a*2+x for x in b))(i, j) for i,j in zip([0,1],[range(4),range(4)])]
In [13]: list(l[0])
Out[13]: [0, 1, 2, 3]
In [14]: list(l[1])
Out[14]: [2, 3, 4, 5]