Re: Peculiar swap behavior

2009-03-05 Thread Aaron Brady
On Feb 23, 12:43 pm, Tim Chase python.l...@tim.thechases.com wrote:
 I stumbled across this oddity and was hoping folks on the list
 might be able to provide a little understanding:

 # swap scalars
   x,y = 1,2
   x,y = y,x
   x,y
 (2, 1)

 # swap lists
   a,b = [1,2,3],[4,5,6]
   a,b = b,a
   a,b
 ([4, 5, 6], [1, 2, 3])

 # swap list contents...not so much...
   m,n = [1,2,3],[4,5,6]
   m[:],n[:] = n,m
   m,n
 ([4, 5, 6], [4, 5, 6])
snip

This may help:

 a= [1,2,3]
 id(a)
12170584
 id(a[:])
12235520

However, I'm not thinking in English at the moment.
--
http://mail.python.org/mailman/listinfo/python-list


Re: Peculiar swap behavior

2009-03-04 Thread Terry Reedy

Tim Chase wrote:
I stumbled across this oddity and was hoping folks on the list might be 
able to provide a little understanding:


# swap scalars
  x,y = 1,2
  x,y = y,x
  x,y
(2, 1)

# swap lists
  a,b = [1,2,3],[4,5,6]
  a,b = b,a
  a,b
([4, 5, 6], [1, 2, 3])

# swap list contents...not so much...
  m,n = [1,2,3],[4,5,6]
  m[:],n[:] = n,m
  m,n
([4, 5, 6], [4, 5, 6])


The first two work as expected but the 3rd seems to leak some internal 
abstraction. 


This works exactly as documented.  Read carefully...

An assignment statement evaluates the expression list (remember that 
this can be a single expression or a comma-separated list, the latter 
yielding a tuple) and assigns the single resulting object to each of the 
target lists, from left to right.

...
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 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.

[*** emphasis added]

and ask What is the value of m when used to replace the contents of n?

tjr


--
http://mail.python.org/mailman/listinfo/python-list


Re: Peculiar swap behavior

2009-03-04 Thread Lie Ryan

andrew cooke wrote:

Delaney, Timothy (Tim) wrote:

Tim Chase wrote:

# swap list contents...not so much...
  m,n = [1,2,3],[4,5,6]
  m[:],n[:] = n,m
  m,n
([4, 5, 6], [4, 5, 6])

[...]

For these types of things, it's best to expand the code out. The
appropriate expansion of:
m,n = [1,2,3],[4,5,6]
m[:],n[:] = n,m
is:
m = [1,2,3]
n = [4,5,6]
m[:] = n
n[:] = m
[...] OTOH, for:
m,n = [1,2,3],[4,5,6]
m[:],n[:] = n[:],m[:]
the expansion is more like:
m = [1,2,3]
n = [4,5,6]
rhs1 = n[:]
rhs2 = m[:]
m[:] = rhs1
n[:] = rhs2


Maybe I'm just being stupid, but you don't seem to have explained
anything.  Isn't the question: Why is the expansion different for the two
cases?  Why don't both expand to have the intermediate rhs variables?

Andrew




Actually the expansion is the same for all case:

Pseudo notation:
m = `m obj`
n = `l obj`
rhs1 = `n rhs`
rhs2 = `m rhs`
`m lhs` = rhs1
`n lhs` = rhs2

where `m rhs` and `n rhs` is whatever you put on the right hand side of 
the assingment; and `m lhs` and `n lhs` is whatever you put on the left 
hand side of the assignment.


i.e.:
m, n = [1, 2, 3], [4, 5, 6]
m[:], n[:] = n, m

is

m = [1, 2, 3]
n = [4, 5, 6]
rhs1 = n
rhs2 = m
m[:] = rhs1
n[:] = rhs2

while:
m, n = [1, 2, 3], [4, 5, 6]
m[:], n[:] = n[:], m[:]

is

m = [1, 2, 3]
n = [4, 5, 6]
rhs1 = n[:]
rhs2 = m[:]
m[:] = rhs1
n[:] = rhs2

while:

m, n = [1, 2, 3], [4, 5, 6]
m, n = n, m

is
m = [1, 2, 3]
n = [4, 5, 6]
rhs1 = n
rhs2 = m
m = rhs1
n = rhs2

while:
m, n = [1, 2, 3], [4, 5, 6]
m, n = n[:], m[:]

is

m = [1, 2, 3]
n = [4, 5, 6]
rhs1 = n[:]
rhs2 = m[:]
m = rhs1
n = rhs2
--
http://mail.python.org/mailman/listinfo/python-list


RE: Peculiar swap behavior

2009-02-23 Thread Delaney, Timothy (Tim)
Tim Chase wrote:

 # swap list contents...not so much...
   m,n = [1,2,3],[4,5,6]
   m[:],n[:] = n,m
   m,n
 ([4, 5, 6], [4, 5, 6])
 
 
 The first two work as expected but the 3rd seems to leak some
 internal abstraction.  It seems to work if I force content-copying:
 
   m[:],n[:] = n[:],m[:]
 
 or even just
 
   m[:],n[:] = n,m[:]
 
 but not
 
   m[:],n[:] = n[:],m
 
 Is this a bug, something Python should smack the programmer for
 trying, or just me pushing the wrong edges? :)

For these types of things, it's best to expand the code out. The
appropriate expansion of:

m,n = [1,2,3],[4,5,6]
m[:],n[:] = n,m

is:

m = [1,2,3]
n = [4,5,6]
m[:] = n
n[:] = m

After the third line, m == n, giving the behaviour you see. OTOH, for:

m,n = [1,2,3],[4,5,6]
m[:],n[:] = n[:],m[:]

the expansion is more like:

m = [1,2,3]
n = [4,5,6]
rhs1 = n[:]
rhs2 = m[:]
m[:] = rhs1
n[:] = rhs2

Tim Delaney
--
http://mail.python.org/mailman/listinfo/python-list


RE: Peculiar swap behavior

2009-02-23 Thread andrew cooke
Delaney, Timothy (Tim) wrote:
 Tim Chase wrote:
 # swap list contents...not so much...
   m,n = [1,2,3],[4,5,6]
   m[:],n[:] = n,m
   m,n
 ([4, 5, 6], [4, 5, 6])
[...]
 For these types of things, it's best to expand the code out. The
 appropriate expansion of:
 m,n = [1,2,3],[4,5,6]
 m[:],n[:] = n,m
 is:
 m = [1,2,3]
 n = [4,5,6]
 m[:] = n
 n[:] = m
 [...] OTOH, for:
 m,n = [1,2,3],[4,5,6]
 m[:],n[:] = n[:],m[:]
 the expansion is more like:
 m = [1,2,3]
 n = [4,5,6]
 rhs1 = n[:]
 rhs2 = m[:]
 m[:] = rhs1
 n[:] = rhs2

Maybe I'm just being stupid, but you don't seem to have explained
anything.  Isn't the question: Why is the expansion different for the two
cases?  Why don't both expand to have the intermediate rhs variables?

Andrew


--
http://mail.python.org/mailman/listinfo/python-list


Re: Peculiar swap behavior

2009-02-23 Thread Robert Kern

On 2009-02-23 16:17, andrew cooke wrote:

Delaney, Timothy (Tim) wrote:

Tim Chase wrote:

# swap list contents...not so much...
m,n = [1,2,3],[4,5,6]
m[:],n[:] = n,m
m,n
([4, 5, 6], [4, 5, 6])

[...]

For these types of things, it's best to expand the code out. The
appropriate expansion of:
 m,n = [1,2,3],[4,5,6]
 m[:],n[:] = n,m
is:
 m = [1,2,3]
 n = [4,5,6]
 m[:] = n
 n[:] = m
[...] OTOH, for:
 m,n = [1,2,3],[4,5,6]
 m[:],n[:] = n[:],m[:]
the expansion is more like:
 m = [1,2,3]
 n = [4,5,6]
 rhs1 = n[:]
 rhs2 = m[:]
 m[:] = rhs1
 n[:] = rhs2


Maybe I'm just being stupid, but you don't seem to have explained
anything.  Isn't the question: Why is the expansion different for the two
cases?  Why don't both expand to have the intermediate rhs variables?


n[:] on the RHS makes a copy of the list.

--
Robert Kern

I have come to believe that the whole world is an enigma, a harmless enigma
 that is made terrible by our own mad attempt to interpret it as though it had
 an underlying truth.
  -- Umberto Eco

--
http://mail.python.org/mailman/listinfo/python-list


Re: Peculiar swap behavior

2009-02-23 Thread Chris Rebert
On Mon, Feb 23, 2009 at 10:43 AM, Tim Chase
python.l...@tim.thechases.com wrote:
snip
 # swap list contents...not so much...
 m,n = [1,2,3],[4,5,6]
 m[:],n[:] = n,m
 m,n
 ([4, 5, 6], [4, 5, 6])

Pseudo-C-Python expansion:
#evaluate RHS. simply *take pointers* since the RHS is just plain variables
ptr_n = n
ptr_m = m
#perform assignments to LHS
#remember that x[y] = z is really a method call to __setitem__ in disguise
#let's pseudo-expand the method calls
#also remember that the assignments must be serialized (Python isn't
magically parallel)
m._items  = (*ptr_n)._items #uh-oh! this just clobbered n._items!
since we only kept a pointer to n, n._items is now gone forever!
n._items = (*ptr_m)._items #uh-oh! this now has no real effect since
we clobbered m._items
#badness!

 The first two work as expected but the 3rd seems to leak some internal
 abstraction.  It seems to work if I force content-copying:

 m[:],n[:] = n[:],m[:]

Pseudo-C-Python expansion:
#evaluate RHS. it's more complicated this time. this matters significantly!
#remember that x[y] is really a *method call* in disguise to __getitem__
#let's pseudo-expend the method-calls too
#note that we *make copies* instead of just taking pointers this time!
n_cpy = copy_list(n)
m_cpy = copy_list(m)
#same principles as before
m._items  = n_cpy._items #but we have a copy of m._items in m_cpy, so
m._items isn't lost forever!
n._items = m_cpy._items #yay, it works!
#huzzah

snip

 Is this a bug, something Python should smack the programmer for trying, or
 just me pushing the wrong edges? :)

Little from column A, little from column B. Using the swapping
assignment works slightly unintuitively with list slices because
copying doesn't magically take place; you need to do this copying
explicitly whereas it's sorta done for you with plain variable
swapping. Just keep in mind that container contents work
by-reference rather than by-value, so to speak.

Cheers,
Chris

-- 
Follow the path of the Iguana...
http://rebertia.com
--
http://mail.python.org/mailman/listinfo/python-list


RE: Peculiar swap behavior

2009-02-23 Thread andrew cooke
andrew cooke wrote:
 Delaney, Timothy (Tim) wrote:
 Tim Chase wrote:
 # swap list contents...not so much...
   m,n = [1,2,3],[4,5,6]
   m[:],n[:] = n,m
   m,n
 ([4, 5, 6], [4, 5, 6])
 [...]
 For these types of things, it's best to expand the code out. The
 appropriate expansion of:
 m,n = [1,2,3],[4,5,6]
 m[:],n[:] = n,m
 is:
 m = [1,2,3]
 n = [4,5,6]
 m[:] = n
 n[:] = m
 [...] OTOH, for:
 m,n = [1,2,3],[4,5,6]
 m[:],n[:] = n[:],m[:]
 the expansion is more like:
 m = [1,2,3]
 n = [4,5,6]
 rhs1 = n[:]
 rhs2 = m[:]
 m[:] = rhs1
 n[:] = rhs2

 Maybe I'm just being stupid, but you don't seem to have explained
 anything.  Isn't the question: Why is the expansion different for the two
 cases?  Why don't both expand to have the intermediate rhs variables?

To answer my own question - you can rewrite all cases to use rhs1, rhs2.

The point is that when you do that: one case (m, n = n, m) reassigns
variables; one case (m[:], n[:] = n, m) mutates one list to be equal to
the other; one case (m[:], n[:] = n[:], m[:]) avoids seeing the effects of
the mutation because a copy is generated.

Which is what other people said, I guess.

Andrew


--
http://mail.python.org/mailman/listinfo/python-list


Re: Peculiar swap behavior

2009-02-23 Thread John Posner
  m,n = [1,2,3],[4,5,6]
  m[:],n[:] = n,m

I believe this is an RTFM situation. In the Python 2.6.1 help topic Simple 
Statements  Assignment Statements, see this para:
snip
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.
/snip
The crucial sentence is:
 Finally, the sequence object is asked to replace the slice with the items
 of the assigned sequence.
In our example, this means that the assignment of the 3-item slicing
m[:] = n
... has this result:
 * the identifier m[0] now refers to the same object as the identifier n[0]
 * the identifier m[1] now refers to the same object as the identifier n[1]
 * the identifier m[2] now refers to the same object as the identifier n[2]
Although m and n are still different list objects, all of the slots in both 
lists now contain the same (sub)objects.
I've verified the above analysis to my own satisfaction using IDLE 2.6.1:
-
def list_ids(mylist):
return id(mylist), map(id, mylist)

m,n = [1,2,3],[4,5,6]
print m,n

print m:, list_ids(m)
print n:, list_ids(n)

m[:], n[:] = n, m
print m,n

print m:, list_ids(m)
print n:, list_ids(n)
-

output:

[1, 2, 3] [4, 5, 6]
m: (4589728, [10970688, 10970676, 10970664])
n: (11311344, [10970652, 10970640, 10970628])
[4, 5, 6] [4, 5, 6]
m: (4589728, [10970652, 10970640, 10970628])
n: (11311344, [10970652, 10970640, 10970628])


-John
--
http://mail.python.org/mailman/listinfo/python-list


Re: Peculiar swap behavior

2009-02-23 Thread Gabriel Genellina
En Mon, 23 Feb 2009 22:58:44 -0200, John Posner jjpos...@snet.net  
escribió:



 m,n = [1,2,3],[4,5,6]
 m[:],n[:] = n,m


I believe this is an RTFM situation. In the Python 2.6.1 help topic  
Simple Statements  Assignment Statements, see this para:


Yes, the other relevant paragraph is:

An assignment statement evaluates the expression list (remember that  
this can be a single expression or a comma-separated list, the latter  
yielding a tuple) and assigns the single resulting object to each of the  
target lists, from left to right.


Note that the RHS is evaluated COMPLETELY before assignment to the LHS  
begins. The assignment:


a, b = c, d

is NOT equivalent to:

a = c
b = d

except in simple cases.

--
Gabriel Genellina

--
http://mail.python.org/mailman/listinfo/python-list