简体   繁体   中英

What does this Python statement mean?

I'm writing a parser, and in the process of debugging it, I found that apparently, this is legal Python:

for []    in [[]]: print 0

and so is this (!):

for [][:] in [[]]: print 0

I don't blame the parser for getting confused... I'm having trouble figuring out how to interpret it!

What exactly does this statement mean?

In terms of execution: nothing.

The for loop itself loops over an empty list, so no iterations will take place.

And that's a good thing, because the for [] means: assign each entry in the loop to 0 variables. The latter part is probably what is puzzling you.

The statement is legal because the target token token_list allows you to assign the values in a sequence to an equally large sequence of variable names; we call this tuple unpacking. The following are more useful examples of target lists, in assignment and deletion:

(a, b, c) = range(3)
del a, b, c

You can do the same in a for loop:

nested = [[1,2,3], [4,5,6]]
for a, b, c in nested:
    print nested

You can use both tuples and lists for the target_list token, this is legal too:

[a, b] = (1, 2)

However, in Python, a list can be empty. Thus, the following is legal, however non-sensical:

[] = []

and finally, so is this:

nested_empty = [[], [], []]
for [] in nested_empty:
    pass

More fun with target lists:

[][:] = [1, 2, 3]

Now the left-hand side is using a slice in the assignment. From the documentation:

If the target is a slicing: The primary expression in the reference is evaluated. It should yield a mutable sequence object (such as a list). The assigned object should be a sequence object of the same type. Next, the lower and upper bound expressions are evaluated, insofar they are present; defaults are zero and the sequence's length. The bounds should evaluate to (small) integers. If either bound is negative, the sequence's length is added to it. The resulting bounds are clipped to lie between zero and the sequence's length, inclusive. Finally, the sequence object is asked to replace the slice with the items of the assigned sequence. The length of the slice may be different from the length of the assigned sequence, thus changing the length of the target sequence, if the object allows it.

So here we are no longer using tuple unpacking; instead we replace a section of the left-hand list with the right-hand list. But because in our example the left-hand list is an anonymous list literal, the resulting altered list is lost again.

But because such an assignment is also legal in a for loop, the following is legal syntax, albeit rather non-sensical:

for [][:] in [range(i) for i in range(10)]: print 0
for [] in [[]]: print 0

it is equivalent to:

In [44]: for [x,y] in [[1,2],[3,4],[5,6]]: # or even (x,y) will work
    print x,y
   ....:     
   ....:     
1 2
3 4
5 6

but the former one expects that no value is returned from the list , i,e the values inside the list are either empty or their len() is 0 .

You can't use () there as it's not a valid.

because in python, you can alss assign like this :

In [56]: x,y=[1,2]      #this means that the expression on RHS should return two values
                        # (x,y)=[1,2] or x,y=(1,2) or (x,y)=(1,2) all are valid

In [57]: x
Out[57]: 1

In [58]: y
Out[58]: 2


In [62]: x,y='ab'     #assign a to x and b to y

In [63]: x
Out[63]: 'a'

In [64]: y
Out[64]: 'b'

Here's my best guess:

for [] in [[]] means "for each instance of [] (an empty list object) in this list [[]] (a list with exactly one element, which is an empty list object), print 0 .

In the second case, I think [:] will just call slice() with all default values, which will simply take a slice of the entire list. Internally that might do something, for instance make a copy of the list object, but the effect in this case should be the same.

The for..in structure is described in the Python manual

http://docs.python.org/reference/compound_stmts.html#the-for-statement

You can have multiple variables to the left of the in keyword

for [i,j] in [(1,2),(3,4),(5,6)]:
  print i, j

for [i,j] in [[1,2],[3,4],[5,6]]:
  print i, j

the manual said that it is interpreted like

i,j = (1,2)

for the first iteration and so on. Thus, you can have an empty list of variables since the list iterated have an empty list as only element. This loop will print 0 once.

The parser you are reading, have it been generated automatically ? This kind of statement can be generated by a non-human source. I don't see it's purpose.

for []    in [[]]: print 0

means that for each empty iterables in [[]], that is a list which contains an empty list, print 0. It's not only limited to lists, but every iterables can be put into that. For example, you can try:

# empty list, empty tuple, empty string, empty unicode
for [] in [[], (), '', unicode()]: print 0

and it would print 0 four times.

[][:] is just the same as []. It will return an empty list, so my answer is the same as above.

Suppose you have a list of tuples that looks like this:

L = [(1,2), (3,4), (5,6)]

Suppose you want to print these tuples in some special way:

for tup in L:
    a = tup[0]
    b = tup[1]
    if a<b:
        print a,b
    else:
        print b,a

But assigning a and b explicitly to be the contents of tup is rather tedious. So you might do this:

for tup in L:
    a,b = tup
    if a<b:
        print a,b
    else:
        print b,a

But you could make it even less tedious:

for (a,b) in L: # you could also do "for [a,b] in L"
    if a<b:
        print a,b
    else:
        print b,a

Here, (a,b) pattern matches against the element that is returned by the iteration. In the first execution of the for loop, the element being returned by iteration is (1,2) , which gets pattern matched against (a,b) , which therefore assigns 1 to a and 2 to b

Now, in your first example, you are iterating over a list containing an empty list. This means that you are trying to print as many 0 s as there are [] s in this list. But it's a little more complex than that:

When you try to pattern match as in my third example, python iterates over the list (or tuple) of variables and the element returned by the iterator, simultaneously, assigning values as they go. So when your pattern contains no variables, then the element that you're trying to pattern match against, should also be empty (and iterable). This explains the following behavior:

>>> for i in 5: print i
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable

>>> for [] in [[], 5]: print 0
... 
0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable

… and this behavior too:

>>> x,y = (2,5,3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack

>>> for [] in [[], [5]]: print 0
... 
0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack

Now, as for your second example, the [:] operation basically creates a copy of the list it's called on, so that altering the original list does not alter the copy and vice versa:

>>> L = [1,2,3]
>>> M = L
>>> M[0] = 'a'
>>> print L[0]
'a'

>>> L = [1,2,3]
>>> M = L[:]
>>> M[0] = 'a'
>>> print L[0]
1

So when you call [][:] , all you're doing is making a new empty list, which works the same as my explanation for your first example

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM