简体   繁体   中英

Why do these two forms of iteration produce different results?

alist = ['1', '2', '3']
blist = alist[:]

for x in alist:
    if not "@gmail.com" in alist:
        x = x + "@gmail.com"

for x in range(len(blist)):
    if not "@gmail.com" in blist[x]:
        blist[x] = blist[x] + "@gmail.com"

The first block of code does not implement what I need, yet the second one does.

What is the difference between these two blocks of code?

Shorty: In first sample you aren't mutating list values, in second example you do.

In first loop you modifying x , which actually is copy of list item, but not that item, in second sample you modifying item of list by accessing it by index. See Immutable vs Mutable types to get more info regarding mutable and immutable types

However if you would apply loops on list which contain mutable types, the items would be modified by both for loops:

alist = [['1'], ['2'], ['3']]
blist = [['1'], ['2'], ['3']]

for x in alist:
    x.append("@gmail.com")
print alist    

for x in range(len(blist)):
    blist[x].append("@gmail.com")
print blist

When you do x = x + "@gmail.com" in the first version of the code, you're creating a new value and rebinding the name x to refer to it. This has no effect on alist , even though that's where the previous x value came from.

When you do blist[x] = blist[x] + "@gmail.com" on the other hand, you're explicitly modifying the list. You're rebinding blist[x] to refer to a new value.

Note that with different list contents, you might have been able to make the first version of the code work, using "in place" modification. Strings are immutable though, so there are no in-place operations. However, if alist contained mutable items such as list s, code like x += ["foo"] would extend the inner list in place. The += operator will attempt to do an in place addition if the type of the object supports it (by having an __iadd__ method). For types that don't support inplace operations though, it's just the same as x = x + y , which will have the same issue you've encountered.

Your fist loop can be fixed to be:

for i, x in enumerate(alist):
    if "@gmail.com" not in x:
        alist[i] = x + "@gmail.com"

Or, more succinctly:

email="@gmail.com"
alist=[x+email if email not in x else x for x in alist]

Since in will return true if that string is anywhere in the string, not x.endswith("@gmail.com") is preferred in both cases above.

Because strings are immutable:

https://docs.python.org/2/reference/datamodel.html

Objects whose value can change are said to be mutable; objects whose value is unchangeable once they are created are called immutable. (The value of an immutable container object that contains a reference to a mutable object can change when the latter's value is changed; however the container is still considered immutable, because the collection of objects it contains cannot be changed. So, immutability is not strictly the same as having an unchangeable value, it is more subtle.) An object's mutability is determined by its type; for instance, numbers, strings and tuples are immutable, while dictionaries and lists are mutable.

In the first example, you are creating a new variable named x each time you concatenate additional values to it.

I the second example, however, you are simply changing the value at the same index of your list .

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