Newbie: tuple list confusion.

Q

Q852913745

I am trying to learn python from 'Python programming for the absolute beginner'
I'm sure I understand the difference between tuples and lists, but while
experimenting with what I have learned so far, I was suprised that this code
worked.

# Create list a
a=["start",878,"item 1",564354,"item 2"]
print "length of list a =",len(a)
print "a =",a ; print

# Create tuple b
b=(234,"item 3",456,"end")
print "length of tuple b =",len(b)
print "b =",b ; print

print "Add b onto a" # Shouldn't work!
#---------------------------------------------
# a=a+b # causes an error as it should,
a+=b # but why is this ok?
#---------------------------------------------
print "a =",a
print "length of a =",len(a)
print;c=raw_input("Press enter to exit")

My book states that a=a+b and a+=b are the same, and that different 'types' of
sequence cannot be joined together.
 
J

John Roth

Q852913745 said:
I am trying to learn python from 'Python programming for the absolute
beginner'
I'm sure I understand the difference between tuples and lists, but while
experimenting with what I have learned so far, I was suprised that this
code
worked.

# Create list a
a=["start",878,"item 1",564354,"item 2"]
print "length of list a =",len(a)
print "a =",a ; print

# Create tuple b
b=(234,"item 3",456,"end")
print "length of tuple b =",len(b)
print "b =",b ; print

print "Add b onto a" # Shouldn't work!
#---------------------------------------------
# a=a+b # causes an error as it should,
a+=b # but why is this ok?
#---------------------------------------------
print "a =",a
print "length of a =",len(a)
print;c=raw_input("Press enter to exit")

My book states that a=a+b and a+=b are the same, and that different
'types' of
sequence cannot be joined together.

This is a simplification. a+=b can be done with an in-place update
for mutable objects, such as lists. An in place update doesn't have
to do exactly the same operation as the equivalent binary operator,
and it seems like it doesn't here.

Since it's an in-place operation, I would expect that a+=b
would be the same as a.append(b), but I haven't checked
that this is the case, and the manual doesn't indicate that
append() accepts general sequences. It does, however,
indicate that at one time it had to accept tuples, and likely
the code to do that was never removed. See note 2
in the Library Reference Manual under Builtin Objects -
Builtin Types - Sequence Types - Mutable Sequence Types.

I wouldn't depend on this working in future releases.

John Roth
 
D

Dan Perl

I would have expected the same results as you, but then, after all, I'm
still a newbie myself.

But here is how I understand it now. a=a+b and a+=b are not the same. The
first one creates a new list and assigns it back to 'a'. The second one is
changing 'a' in-place and is probably equivalent to a.extend(b).

Here is some code to prove that a is changed in-place in the second case:
a=[1,2,3]
c=a
print a,c # [1, 2, 3] [1, 2, 3], a and c are the same
object
a.append(4)
print a,c # [1, 2, 3, 4] [1, 2, 3, 4], I told you so!
b=[5,6,7]
a=a+b
print a,c # [1, 2, 3, 4, 5, 6, 7] [1, 2, 3, 4], a and
c are not the same object anymore
a=c
print a,c # [1, 2, 3, 4] [1, 2, 3, 4], a and c are the
same object again
a+=b
print a,c # [1, 2, 3, 4, 5, 6, 7] [1, 2, 3, 4, 5, 6,
7], still the same object!
b=(8,9) # let's show it with a tuple too
a+=b
print a,c # [1, 2, 3, 4, 5, 6, 7, 8, 9] [1, 2, 3, 4, 5,
6, 7, 8, 9]


Dan
 
D

Dan Perl

John Roth said:
Q852913745 said:
I am trying to learn python from 'Python programming for the absolute
beginner'
I'm sure I understand the difference between tuples and lists, but while
experimenting with what I have learned so far, I was suprised that this
code
worked.

# Create list a
a=["start",878,"item 1",564354,"item 2"]
print "length of list a =",len(a)
print "a =",a ; print

# Create tuple b
b=(234,"item 3",456,"end")
print "length of tuple b =",len(b)
print "b =",b ; print

print "Add b onto a" # Shouldn't work!
#---------------------------------------------
# a=a+b # causes an error as it should,
a+=b # but why is this ok?
#---------------------------------------------
print "a =",a
print "length of a =",len(a)
print;c=raw_input("Press enter to exit")

My book states that a=a+b and a+=b are the same, and that different
'types' of
sequence cannot be joined together.

This is a simplification. a+=b can be done with an in-place update
for mutable objects, such as lists. An in place update doesn't have
to do exactly the same operation as the equivalent binary operator,
and it seems like it doesn't here.

Since it's an in-place operation, I would expect that a+=b
would be the same as a.append(b), but I haven't checked
that this is the case, and the manual doesn't indicate that
append() accepts general sequences. It does, however,
indicate that at one time it had to accept tuples, and likely
the code to do that was never removed. See note 2
in the Library Reference Manual under Builtin Objects -
Builtin Types - Sequence Types - Mutable Sequence Types.

I wouldn't depend on this working in future releases.

John Roth

John,

I don't think it's an 'append', it's probably an 'extend'.
a=[1,2,3]
a.append((4,5))
print a # [1, 2, 3, (4, 5)]
a.pop() # (4, 5)
a.extend((4,5))
print a # [1, 2, 3, 4, 5]
 
G

George Yoshida

Q852913745 said:
My book states that a=a+b and a+=b are the same, and that different 'types' of
sequence cannot be joined together.

This is because list's += is implemented to work like list's extend
method. Change "a += b" with "a.extend(b)". You get the same result. It
might be counterintuitive, but it is the way '+=' is implemented.

I'll add two pointers. Check them.

* Concatenating a tuple to a list
http://www.python.org/sf/575536
* list += string??
http://mail.python.org/pipermail/python-dev/2004-August/048389.html

- george
 
E

Elaine Jackson

In addition to what you've found out already, there is another difference that,
to my mind, is of paramount importance; namely, the fact that an augmented
assignment (a+=b) is actually not an assignment at all. An actual assignment
(a=a+b) binds the name "a" to a new object, while a so-called augmented
assignment mutates the object a itself. I was quite confused by this point when
I first found out about it, so I'll do my best to explain it now for your
benefit. Consider the following functions:
def f(a): a=a+['tackedOnItem']; return a

## The "a" on the righthand side of the assignment
## evaluates to the function's parameter, since
## at that point no local variable with that name
## has yet been declared; next the "a" on the
## lefthand side of the assignment declares a local
## variable (a function cannot rebind the name of
## one of its parameters); and finally the "a" in
## the return statement evaluates to the value of
## the local variable "a" (local bindings being
## always preferred over global ones when possible).
## Hence f returns x + ['tackedOnItem'], where x
## is its actual parameter, and that parameter is
## NOT mutated.
def g(a): a+=['tackedOnItem']; return a

## Since the augmented assignment is actually a
## mutation, there is no question of rebinding the
## name of the parameter; the "a" on the lefthand
## side of the augmented assignment evaluates to
## the parameter, and the parameter gets mutated;
## there being no local variable called "a", the "a"
## in the return statement also evaluates to the
## parameter, and so g returns its mutated parameter.
def h(a): a.extend(['tackedOnItem']); return a

## This function is equivalent to g, but more explicit about its side-effect.
## Here are the relevant examples:
x = [1,2,3]
f(x) [1, 2, 3, 'tackedOnItem']
x [1, 2, 3]
g(x) [1, 2, 3, 'tackedOnItem']
x [1, 2, 3, 'tackedOnItem']
y = [4,5,6]
h(y) [4, 5, 6, 'tackedOnItem']
y
[4, 5, 6, 'tackedOnItem']

HTH


| I am trying to learn python from 'Python programming for the absolute
beginner'
| I'm sure I understand the difference between tuples and lists, but while
| experimenting with what I have learned so far, I was suprised that this code
| worked.
|
| # Create list a
| a=["start",878,"item 1",564354,"item 2"]
| print "length of list a =",len(a)
| print "a =",a ; print
|
| # Create tuple b
| b=(234,"item 3",456,"end")
| print "length of tuple b =",len(b)
| print "b =",b ; print
|
| print "Add b onto a" # Shouldn't work!
| #---------------------------------------------
| # a=a+b # causes an error as it should,
| a+=b # but why is this ok?
| #---------------------------------------------
| print "a =",a
| print "length of a =",len(a)
| print;c=raw_input("Press enter to exit")
|
| My book states that a=a+b and a+=b are the same, and that different 'types' of
| sequence cannot be joined together.
 
A

Alex Martelli

Elaine Jackson said:
In addition to what you've found out already, there is another difference
that, to my mind, is of paramount importance; namely, the fact that an
augmented assignment (a+=b) is actually not an assignment at all. An
actual assignment (a=a+b) binds the name "a" to a new object, while a
so-called augmented assignment mutates the object a itself.

Ah, careful there: augmented assignment (nothing "so-called" about it;-)
mutates the object and THEN assigns ("re-binds" if you want to get
technical) the name or other slot holding the object.

For example...:
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: object does not support item assignment

If it were true that the += "is actually not an assignment" this
behavior and error message would be impossible. It _IS_ an assignment,
_augmented_ by the fact that the object in question (if mutable) is also
supposed to mutate.


Regarding the fact that the mutation takes place before the assignment,
this is the kind of behavior details you can check by writing a simple
dummy class with the appropriate special method doing a print. For
example:
.... def __iadd__(self, other):
.... print 'iadd running'
.... return self
....
iadd running
Traceback (most recent call last):

See? First we see the message "iadd running", so __iadd__ is executing;
THEN, the error message about assignment to item not being supported.

This is important because the object DOES get mutated even though the
assignment fails. If you check the L in the previous fragment, indeed,
you'll see it's now [1], not [] any more.


Yes, I do agree these subtleties are potentially confusing -- a pity,
because Python's foremost strength is mostly to AVOID being confusing.
But I don't believe we could have designed +='s behavior to be
substantially clearer and yet just as useful as it is today, given all
the rest of the details about the way Python works; and I do believe
+='s usefulness is sufficient to warrant having it in Python.


Alex
 
J

John Roth

Alex Martelli said:
Ah, careful there: augmented assignment (nothing "so-called" about it;-)
mutates the object and THEN assigns ("re-binds" if you want to get
technical) the name or other slot holding the object.

I've never understood why it does that. If you have a mutable object
that is updated, then the rebinding makes no sense whatsoever.
The problem is that it's not just confusing, I think I have a
reasonable expectation that if it fails, it fails atomically.

John Roth
 
A

Alex Martelli

John Roth said:
I've never understood why it does that. If you have a mutable object
that is updated, then the rebinding makes no sense whatsoever.

I guess the point is that:

T = (X,)
T[0] += Y

*MUST* fail, whatever X's type; having it fail when (e.g.) type(X) is
str and succeed when type(X) is list would be strange...
The problem is that it's not just confusing, I think I have a
reasonable expectation that if it fails, it fails atomically.

I guess the problem here is that we can't be sure that the semantics of,
say,
T[x] = T[x]
are to either fail or be a no-operation, and in the latter case future
assignments to T[x] won't fail. That depends on type(T)... and I don't
see other ways to ensure 'atomicity' -- we can't afford to deepcopy X
before calling X.__iadd__, after all, just to try and ensure we can
"undo" the effect of the latter.

If you disagree with the details of T[x] += y chosen semantics (and I
can well see why one might), I think you should get together with others
who agree with you and put together a PEP in time for Python 3000, which
will probably be the one and only chance to introduce incompatible
changes in this matter (like in others). While I can sympathize with
your qualms, I think that preparing a PEP to specify precisely what
should happen in such cases will help you see why the current semantics
were chosen. Similarly for someobj.anattr += whatever and so on, of
course. Python 3000 is years away, so it's not an urgent matter, but
without such a PEP I think I can predict these details won't change.


Alex
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,021
Latest member
AkilahJaim

Latest Threads

Top