Of all the odd quirks of python, this is the only thing that has bitten be in the butt.
And several, several times.
List comprehensions have similar limitations as python lambdas, however, so I guess the only way to execute several expressions on the item in the list would be to pass the item to a function and return the changed item?


Thoughts,
Jacob


On Fri, 11 Feb 2005, Matt Dimmic wrote:

In Python, one bug that often bites me is this:

(example A)
aList = [1,2,3]
for i in aList:
    i += 1
print aList
--> [1,2,3]

This goes against my intuition, which is that aList == [2,3,4], probably
because so much in Python is passed by reference and not by value.


Hi Matt,


Yes. If we "unroll" the loop, we can better see what's going on:

###
aList = [1, 2, 3]
i = aList[0]
i += 1
i = aList[1]
i += 1
i = aList[2]
i += 1
print aList
###

This has the same meaning as Example A, and it should be clearer why the
assignment to 'i' has no affect.  'i' is just a regular local variable,
just like any other local variable.

Assignment is not the same thing as value mutation; it's actually the same
issue that we talked about earlier with the default argument thread.
*grin*



When I see something like:

###
x = 42
y = x
y = y + 1
###


My visual model of this is that variable names are "arrows" to values:

--------------------------------------------------

    x -----------> 42            ##  x = 42

----------------------------------------------------

    x -----------> 42            ## y = x
                   ^
                  /
    y -----------/

----------------------------------------------------

    x -----------> 42            ## y = y + 1

    y -----------> 43

----------------------------------------------------



That being said, we can do something here with a little indirection:

###
def box(x):
...     """Put a mutable wrapper around the value x."""
...     return [x]
...
def unbox(boxedValue):
...     """Grab the value in the box."""
...     return boxedValue[0]
...
def incr(boxedValue):
...     """Increment the value in the box."""
...     boxedValue[0] += 1
...


aList = [box(1), box(2), box(3)]
map(unbox, aList)
[1, 2, 3]
for b in aList:
...     incr(b)
...
map(unbox, aList)
[2, 3, 4]
###

This might be overkill, though.  *grin*

But this shows that, if we really needed to, we could "box" everything in
some kind of mutable container.  I don't recommend this for normal Python
programming, though.



Of course I can always use range() or enumerate():

(example B)
aList = [1,2,3]
for i in range(len(aList)):
    aList[i] += 1
print aList
--> [4,5,6]

But example A seems more elegant, if only it did what I wanted it to do.
:) So pardon my ignorance if the answer is obvious, but what is the
simplest way in Python to get a reference to an element in a list? Is it
really Example B?

A variation of Example B, with enumerate(), is probably the most straightforward way to do in-place mutation on the list in Python:

###
aList = [1, 2, 3]
for i, x in enumerate(aList):
   aList[i] = x + 1
print aList
###

There are languages that magically allow us to do mutation on a list
without making it look like list mutation.  Perl is one of those languages
that adds magic syntactic sugar for doing in-place list stuff.  But Python
has no special mechanisms for doing this.


The main issue that I think people are running against is that, in Python, numeric values are "immutable" --- they can't be changed. When we do things like addition:

###
x = 7
x = x + 1
###

we are not changing the value of '7': we're just "re-aiming" or directing
'x' to the new value '8'.


If you have more questions, please feel free to ask!

_______________________________________________
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor



_______________________________________________ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor

Reply via email to