Le Sun, 01 Feb 2009 16:34:10 +0800,
David <ld...@gmx.net> a écrit :

> Hello list,
> 
> I continue my reading of Harrington. In section 2.4. he is making a 
> point about the need to clone, as otherwise the object associated to 
> parameter corner (the point (20, 50)) takes the same value as corner2. 
> That is: corner2 changes corner, which in turn changes the point (20, 
> 50). 

[...]

(Both point happen to have the same coordinates, actually to *be* the same 
point, that's it?)

> This behaviour stuns me, because I was always following the belief that 
> when variable2 refers to another variable1, the value of variable1 would 
> NOT change, even as I operate on variable2 - just like my little 
> experiment at the promt:
> 
> In [10]: x = 5
> 
> In [11]: y = x
> 
> In [12]: y + 2
> Out[12]: 7
> 
> In [13]: x
> Out[13]: 5
> 
> So here is my question: what have I failed to grasp? Are those different 
> issues? If so, why?
> 
> I'd appreciate it if you could give me a short comment, this one bothers 
> me ;-)
> 
> David

You are not the first one, you won't be the last one ;-)

Actually, python does not make any difference between values and objects: all 
are objects. (Python distinguishes mutable and immutable types, see below about 
that). More precisely, at the conceptual level, there are kinds of data (or 
data constructs), like a "position", that are values, even when compound. For 
instance, a position may be made of x and y simple values.
Let us say you create a custom type of objects such as:

class Point(object):
        def __init__(self, x=0, y=0):
                self.x = x
                self.y = y
        def __str__(self):
                return "(x:%s y:%s)" %(self.x,self.y)

Conceptually, this is in fact a compound value that should rather be called 
"Position". So that when you "copy" a point/position to create a new one, you 
expect each one to have its own value -- what won't happen:

shift_x, shift_y = 20,30
p = Point(100,100)
q = p
# ...
q.x += shift_x ; q.y += shift_y
print p,q
==>
(x:120 y:130) (x:120 y:130)

This occurs because you make a confusion between what is for you a value (be it 
compound), or not, at the conceptual level ; and what it is for python: an 
object in both cases, that has an identity and a state.
When "q = p" is executed an alias name "q" is bound to the same unique Point 
object that was already bound to the name 'p'. That's all what happens. When 
this object's state is further changed, then logically printing p or q outputs 
the same new state. The point is shared by p and q.

The obvious remedy is to copy the point's state, meaning the values it holds, 
instead of copying the object itself:

q = Point()
q.x,q.y = p.x,p.y

Another solution is to use tuples, which are the container type that python 
provides to hold compound values. If you represent points/positions by (x,y) 
tuples, then the program wil behave the way you intend it, meaning that p and q 
will have different positions and actually *be* distinct objects for python:

shift_x, shift_y = 20,30
p = (100,100)
q = p
# ...
q = (q[0]+shift_x, q[1]+shift_y)
print p,q
==>
(100, 100) (120, 130)

The proper distinction here is that tuples are "immutable" objects. This means 
that one cannot change them directly (actually there is some more subtility) by 
writing e.g. q[0]=120. Concretely, for the programmer, it follows that tuples 
and all immutable objects (number,string,boolean) behave like we expect it from 
data that represent values in a program/model.

[There some literature on the topic. And even a design pattern called "value 
object" that adresses the issue.]

Denis

------
la vida e estranya
_______________________________________________
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor

Reply via email to