what is meaning of [iter(list)]*2 in python?

青春壹個敷衍的年華 提交于 2020-05-15 09:25:31

问题


I have found below code in web, result is tuple of two elements in list, how to understand [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>]

I check [iter(lst)]*2, same iterator above, so meaning iter repeat double, so, if i check num from 2 to 3, result should be [(1, 2, 3), (4, 5, 6),(7,8,NaN)] but delete 7,8

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

回答1:


Quite a tricky construct to explain. I'll give it a shot:

with [iter(lst)] you create a list with with one item. The item is an iterator over a list.

whenever python tries to get an element from this iterator, then the next element of lst is returned until no more element is available.

Just try following:

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

the output should look like:

>>> 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

Now you create a list that contains twice exactly the same iterator. You do this with itlst = [iter(lst)] * 2

try out following:

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

The result will look something like:

>>> 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>]

What is important to notice is, that itlst1 is a list containing twice the same iterator, whereas itlst2 contains two different iterators.

to illustrate try to type:

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

and compare it with:

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

The result is:

>>> 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

Now to the zip() function ( https://docs.python.org/3/library/functions.html#zip ):

Try following:

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

zip() with two parameters. Whenver you try to get the next element from zip it will do following:

  • get one value from the iterable that is the first parameter
  • get one value from the iterable that is the second parameter
  • return a tuple with these two values.

list(zip(xxx)) will do this repeatedly and store the result in a list.

The result will be:

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

The next trick being used is the * that is used to use the first element as first parameter to a function call, the second element as second parameter and so forth) What does ** (double star/asterisk) and * (star/asterisk) do for parameters?

so writing:

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

is in this case identical to

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

which is identical to

list(zip(i, i))

which I explained already.

Hope this explains most of the above tricks.




回答2:


iter(lst) turns a list into an iterator. Iterators let you step lazily through an iterable by calling next() until the iterator runs out of items.

[iter(lst)] puts the iterator into a single-element list.

[iter(lst)] * 2 makes 2 copies of the iterator in the list, giving

it = iter(lst)
[it, it] 

Both list elements are aliases of the same underlying iterator object, so whenever next() is called on either of the iterators as zip exhausts them, successive elements are yielded.

*[...] unpacks the list of the two copies of the same iterator into the arguments for zip. This creates a zip object that lets you iterate through tuples of elements from each of its arguments.

list(...) iterates through the zip object and copies the elements into a list. Since both zipped iterators point to the same underlying iterator, we get the sequential elements seen in your output.

Without using the iterator alias, you'd get

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

A similar way to write list(zip(*[iter(lst)] * 2)) is list(zip(lst[::2], lst[1::2])), which seems a bit less magical (if much less performant).

The explanation for

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

omitting elements is that the first time the zip object tries to yield a None result on any of the argument iterables, it stops and does not generate a tuple. You can use itertools.zip_longest to match your expected behavior, more or less:

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

See the canonical answer List of lists changes reflected across sublists unexpectedly if the [...] * 2 aliasing behavior is surprising.



来源:https://stackoverflow.com/questions/61670945/what-is-meaning-of-iterlist2-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!