[Tutor] confusion about cloning (graphics)

spir denis.spir at free.fr
Sun Feb 1 12:45:08 CET 2009


Le Sun, 01 Feb 2009 16:34:10 +0800,
David <ldl08 at 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


More information about the Tutor mailing list