Выражение генератора использует список, назначенный после создания генератора - PullRequest
0 голосов
/ 24 октября 2018

Я нашел этот пример и не могу понять, почему он работает непредсказуемо?Я предполагал, что он должен вывести [1, 8, 15] или [2, 8, 22].

array = [1, 8, 15]
g = (x for x in array if array.count(x) > 0)
array = [2, 8, 22]
print(list(g))


>>>[8]

Ответы [ 5 ]

0 голосов
/ 24 октября 2018

Путаница, как и ответ, заключается в строке: g = (x for x in array if array.count(x) > 0)
Если мы упростим эту строку, то она станет: g = (x for x in array1 if array2.count(x) > 0)

Теперь, когда генератор *Создается 1007 *, затем сохраняется ссылка на объект array1.Таким образом, даже если я изменит значение array1 на любое другое значение (то есть установлю его на новый объект массива), это не повлияет на копию генератора array1.Потому что только array1 меняет ссылку на объект.Но array2 проверяется динамически.Так что, если мы изменим его значение, оно будет отражено.

Вы можете увидеть результат из следующего кода, чтобы понять его.Посмотрите , работающий онлайн здесь :

array1 = [1, 8, 15] #Set value of `array1`
array2 = [2, 3, 4, 5, 8] #Set value of `array2`
print("Old `array1` object ID: " + repr(id(array1)))
print("Old `array2` object ID: " + repr(id(array2)))
g = (x for x in array1 if array2.count(x) > 0)
array1 = [0, 9] #Changed value of `array1`
array2 = [2, 8, 22, 1] #Changed value of `array2`
print("New `array1` object ID: " + repr(id(array1)))
print("New `array2` object ID: " + repr(id(array2)))
print(list(g))

Вывод:

Old `array1` object ID: 47770072262024
Old `array2` object ID: 47770072263816
New `array1` object ID: 47770072263944
New `array2` object ID: 47770072264008
[1, 8]
0 голосов
/ 24 октября 2018

когда вы впервые создаете массив и назначаете элементы в нем, элементы массива указывают на какое-то место в памяти, и генератор сохраняет это местоположение (не массив) для его выполнения.

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

Посмотрите приведенный ниже пример для лучшего понимания

array = [1, 8, 15]
for i in array:
    print(id(i))

g = (x for x in array if array.count(x) > 0)

print('<======>')

array = [2, 8, 22]
for i in array:
    print(id(i))

print(array)
print(list(g))

Выход

140208067495680
140208067495904
140208067496128
<======>
140208067495712
140208067495904 # memory location is still same
140208067496352
[2, 8, 22]
[8]
0 голосов
/ 24 октября 2018

На самом деле, это не совсем безумие, если вы посмотрите более внимательно.посмотрите на

g = (x for x in array if array.count(x) > 0)

, он создаст генератор, который просматривает массив и будет искать, если число уже существующих значений больше нуля.поэтому ваш генератор ищет только 1, 8 и 15, и когда вы изменяете значения на другое, генератор просто ищет предыдущие значения снова, а не новые.потому что он (генератор) создает, когда массив имел их.

, поэтому, если вы поместите тысячи значений в массив, он будет искать только эти три.

0 голосов
/ 24 октября 2018

Из документов Выражения генератора :

Переменные, используемые в выражении генератора, оцениваются лениво , когда __next__() метод вызывается для объекта генератора (так же, как обычные генераторы).Однако итеративное выражение в крайнем левом предложении for равно , которое немедленно оценивается , так что вызванная им ошибка будет сгенерирована в точке, где определено выражение генератора, а нев точке, где извлекается первое значение.

Таким образом, при запуске

array = [1, 8, 15]
g = (x for x in array if array.count(x) > 0)

оценивается только первое array в выражении генератора.x и array.count(x) будут оцениваться только при вызове next(g).Так как вы array указываете на другой список [2, 8, 22] до использования генератора, вы получаете «неожиданный» результат.

array = [2, 8, 22]
print(list(g))  # [8]
0 голосов
/ 24 октября 2018

Причина в том, что во время создания генератор (a for b in c if d) оценивает только c (что иногда делает также предсказуемым b).Но a, b, d оцениваются во время потребления (на каждой итерации).Здесь при оценке d (array.count(x) > 0).

можно использовать текущую привязку array из охватывающей области. Например, можно выполнить:

g = (x for x in [] if a)

Не объявив a заранее.Но вы должны убедиться, что a существует, когда генератор потребляется.

Но вы не можете сделать аналогичным образом:

g = (x for x in a if True)

По запросу:

Вы можете наблюдатьаналогичные (но не идентичные) шаблоны с общей функцией генератора:

def yielder():
    for x in array:
        if array.count(x) > 0:
            yield x

array = [1, 8, 15]
y = yielder()
array = [2, 8, 22]
list(y)
# [2, 8, 22]

Функция генератора не выполняет ни одного из своих элементов до потребления.Следовательно, даже array в заголовке цикла for связывается поздно.Еще более тревожный пример возникает, когда мы «выключаем» array во время итерации:

array = [1, 8, 15]
y = yielder()
next(y)
# 1
array = [3, 7]
next(y)  # still iterating [1, 8, 15], but evaluating condition on [3, 7]
# StopIteration raised
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...