что означает [iter (список)] * 2 в python? - PullRequest
3 голосов
/ 08 мая 2020

Я нашел код ниже в сети, результатом является кортеж из двух элементов в списке, как понять [iter(list)]*2?

lst = [1,2,3,4,5,6,7,8]
b=zip(*[iter(lst)]*2)
list(b)

[(1, 2), (3, 4), (5, 6), (7, 8)]

------------
[iter(lst)]*2
[<list_iterator at 0x1aff33917f0>, <list_iterator at 0x1aff33917f0>]

Я проверяю [iter(lst)]*2, тот же итератор выше, что означает iter повторять дважды, поэтому, если я проверю число от 2 до 3, результат должен быть [(1, 2, 3), (4, 5, 6),(7,8,NaN)], но удалить 7,8

lst = [1,2,3,4,5,6,7,8]
b=zip(*[iter(lst)]*3)
list(b)
--------------
[(1, 2, 3), (4, 5, 6)]

Ответы [ 2 ]

2 голосов
/ 08 мая 2020

Довольно сложная конструкция для объяснения. Я попробую:

с [iter(lst)] вы создаете список с одним элементом. Элемент является итератором по списку.

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

Просто попробуйте следующее:

i = iter(lst)
next(i)
next(i)

результат должен выглядеть так:

>>> lst = [1,2,3,4,5,6,7,8]  
>>> i = iter(lst)
>>> next(i)
1
>>> next(i)
2
>>> next(i)
3
>>> next(i)
4
>>> next(i)
5
>>> next(i)
6
>>> next(i)
7
>>> next(i)
8
>>> next(i)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Теперь вы создаете список, содержащий дважды точно такой же итератор. Вы делаете это с помощью itlst = [iter(lst)] * 2

попробуйте следующее:

itlst1 = [iter(lst)] * 2
itlst2 = [iter(lst), iter(lst)]
print(itlst1)
print(itlst2)

Результат будет выглядеть примерно так:

>>> itlst1 = [iter(lst)] * 2
>>> itlst2 = [iter(lst), iter(lst)]
>>> print(itlst1)
[<list_iterator object at 0x7f9251172b00>, <list_iterator object at 0x7f9251172b00>]
>>> print(itlst2)
[<list_iterator object at 0x7f9251172b70>, <list_iterator object at 0x7f9251172ba8>]

Важно отметить, что itlst1 - это список, содержащий дважды один и тот же итератор, тогда как itlst2 содержит два разных итератора.

для иллюстрации попробуйте ввести:

next(itlst1[0])
next(itlst1[1])
next(itlst1[0])
next(itlst1[1])

и сравнить его с:

next(itlst2[0])
next(itlst2[1])
next(itlst2[0])
next(itlst2[1])

Результат:

>>> next(itlst1[0])
1
>>> next(itlst1[1])
2
>>> next(itlst1[0])
3
>>> next(itlst1[1])
4
>>> 
>>> next(itlst2[0])
1
>>> next(itlst2[1])
1
>>> next(itlst2[0])
2
>>> next(itlst2[1])
2

Теперь к функции zip() (https://docs.python.org/3/library/functions.html#zip):

Попробуйте следующее:

i = iter(lst)
list(zip(i, i))

zip() с двумя параметрами. Когда вы пытаетесь получить следующий элемент из zip, он будет делать следующее:

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

list(zip(xxx)) будет делать это повторно и сохранять результат в списке.

Результат будет:

>>> i = iter(lst)
>>> list(zip(i, i))
[(1, 2), (3, 4), (5, 6), (7, 8)]

Следующий используемый трюк - это *, который используется для использования первого элемента в качестве первого параметра вызова функции, второго элемента в качестве второго параметра и т. д.) Что ** (двойная звезда / звездочка) и * (звездочка / звездочка) делают для параметров?

поэтому запись:

itlst1 = [iter(lst)] * 2
list(zip(*itlst1))

в этом случае идентична

i = iter(lst)
itlst1 = [i] * 2
list(zip(itlst1[0], itlst[1]))

, что идентично

list(zip(i, i))

, которое я уже объяснял.

Надеюсь, это объясняет большинство вышеперечисленных трюков.

1 голос
/ 08 мая 2020

iter(lst) превращает список в итератор . Итераторы позволяют вам лениво проходить через итерацию, вызывая next(), пока в итераторе не закончатся элементы.

[iter(lst)] помещает итератор в одноэлементный список.

[iter(lst)] * 2 создает 2 копии итератора в списке, что дает

it = iter(lst)
[it, it] 

Оба элемента списка являются псевдонимами одного и того же базового объекта итератора, поэтому всякий раз, когда next() вызывается на любом из итераторов как zip истощает их, последовательные элементы уступают место.

*[...] распаковывает список двух копий одного и того же итератора в аргументы для zip. Это создает zip-объект, который позволяет вам перебирать кортежи элементов из каждого из его аргументов.

list(...) выполняет итерацию через zip-объект и копирует элементы в список. Поскольку оба заархивированных итератора указывают на один и тот же базовый итератор, мы получаем последовательные элементы, видимые в вашем выводе.

Без использования псевдонима итератора вы получите

>>> list(zip(iter(lst), iter(lst)))
[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8)]

Аналогичный способ write list(zip(*[iter(lst)] * 2)) равно list(zip(lst[::2], lst[1::2])), что кажется немного менее волшебным (хотя и менее эффективным).

Объяснение пропуска элементов

>>> list(zip(*[iter(lst)] * 3))
[(1, 2, 3), (4, 5, 6)]

состоит в том, что первый раз zip-объект пытается выдать результат None для любого из итераций аргумента, он останавливается и не создает кортеж. Вы можете использовать itertools.zip_longest для соответствия вашему ожидаемому поведению, более или менее:

>>> list(zip_longest(*[iter(lst)] * 3))
[(1, 2, 3), (4, 5, 6), (7, 8, None)]

См. Канонический ответ Список изменений списков неожиданно отражается в подсписках если поведение наложения [...] * 2 вызывает удивление.

...