pythonpython-3.xlistenumerate

How does [string for i, string in enumerate(a)] work in python?


Thanks for helping me! My question been answered in the comments, by @juanpa.arrivillaga . Key point of my question is about how python know "I am trying to assign the first element to i and the second element to string".


I am learning python, but how does [string for i, string in enumerate(a)] work really troubles me.

Here are 2 example code I write while learning python

a = ['a','b','c', 'd']
d = [string for string in enumerate(a)]
print (d)

it would return '[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]'. However, if I write the following code

a = ['a','b','c', 'd']

c = [string for i, string in enumerate(a)]
print(c)

it would return ['a', 'b', 'c', 'd']

In my understanding, string in enumerate(a) would translated to the "enumerated object" like position inside RAM. and I do not tell python what the i is (I did not tell python i is the index or string is the element). So, the code would be explained as "list["enumerated object" iterate inside "i"]", but since I did not tell python what i is, it will run into error. However, it returned a list of elements from enumerate.

Where do python learned from my code the string means element inside enumerate, and i means the index?


Solution

  • So, two things you need to understand. First, what is enumerate and second, how iterable unpacking works.

    enumerate takes an iterable as it's first argument and a start keyword argument (that defaults to 0 if not passed explicitly). It returns an iterator of pairs, i.e. of tuples of length 2, where the first element are ints starting at start and increasing by one, and the second element comes from the original iterable you passed to enumerate.

    >>> iterable = ["foo", "bar", "baz"]
    >>> iterator = enumerate(iterable, start=1)
    >>> next(iterator)
    (1, 'foo')
    >>> next(iterator)
    (2, 'bar')
    >>> list(iterator)
    [(3, 'baz')]
    

    Now, you need to understand iterable unpacking in assignment targets. Note, in the for clause of a for-loop or list comprehension (or dict/set comprehensions or generator expressions), you are assigning to a target. So:

    >>> for x in range(4):
    ...     print(x)
    ...
    0
    1
    2
    3
    >>> print("see, x is just a regular variable", x)
    see, x is just a regular variable 3
    

    Because, at the beginning of each iteration, you get the next value from the iterator created by the iterable in the in clause. Anything that goes in:

    for <assignment target> in iterable
    

    Can also go:

    <assignment target> = whatever
    

    But assignment targets can be more than a simple, single variable. The simplest extension, unpack into exactly two variables:

    >>> x = 3
    >>> x, y = "XY"
    >>> x
    'X'
    >>> y
    'Y'
    >>> x, y = range(2)
    >>> x
    0
    >>> y
    1
    >>> x, y = ['foo', 'bar']
    >>> x
    'foo'
    >>> y
    'bar'
    

    It can get more elaborate than that but I think you already understand that.

    So for i, string in whatever is you telling Python:

    I expect every element in whatever (itself an iterable) to be some iterable with exactly two elements. On each iteration, assign the first item to i and the second to string. This is simply based on position, the names don't matter. They could be for banana, apple in whatever and it works the exact same way.