[Tutor] Still confused about Python references/objects

Tim Peters tim.one@home.com
Sat, 31 Mar 2001 14:52:00 -0500


[Sheila King]
> ...
> However, just for illustration, I was trying to write a swap
> function. And, I thought that all parameters were passed by reference.

"by reference" isn't really a useful concept in Python.  People bring that
with them from other languages, and get into all sorts of trouble trying to
force it to fit.  Python is *simpler* than that.  It's easier (in the end
<wink>) to think of argument-passing in Python as being "pass by object":
don't think of passing names, or pointers, or references, or even values, but
think of passing objects directly.

When you do f(x), f is passed the precise object to which the name "x" refers
at the time.  f doesn't see the *name* "x", nor does it get a copy of the
value of the object x refers to, it gets the exact object x refers to at the
time.

The other half of the story is that a statement of the form

    x = y

is *not* an operation on objects, it's an operation on namespaces, and all it
means is "whatever object the name 'y' refers to at this moment, also give
that object the name 'x' (and in the local namespace, provided 'x' hasn't
been declared global)".

Argument passing is exactly like

    x = y

in all respects:  the objects in the argument list are merely given names in
the called function's local namespace.  The names they're given are, of
course, the function's formal argument names.

> So, when I ran the following interpreter session, I was really
> surprised to find that my swap function actually swapped nothing at all:

Oh, but it did -- just not where you thought <wink>.

> Python 2.0 (#8, Oct 16 2000, 17:27:58) [MSC 32 bit (Intel)] on win32
> Type "copyright", "credits" or "license" for more information.
> IDLE 0.6 -- press F1 for help
> >>> x = 2
> >>> y = 3
> >>> print x, " ", y
> 2   3
> >>> (y, x) = (x, y)
> >>> print x, " ", y
> 3   2
> >>> def swap(a, b):
> 	(b, a) = (a, b)
>
> >>> swap(x, y)

Calling "swap(x, y)" has two effects at the start:

1. Attaches the name 'a' in swap's local namespace to the object
   named by 'x' in the call (which is the integer object 3).

2. Attaches the name 'b' in swap's local namespace to the object
   named by 'y' in the call (which is the integer object 2).

So the body of swap begins life with two local names, a and b, exactly as if
the lines

   a = 3
   b = 2

had appeared in its body.  These have *nothing* to do with the names 'x' and
'y' in the caller anymore -- the names x and y don't live in swap's lcoal
namespace, and assignments within swap can only affect swap's local
namespace.

So the

    (b, a) = (a, b)

line does swap what the names "a" and "b" refer to in *swap's* local
namespace, but, again, that has nothing to do with any other namespace, and
in particular has nothing to do with the caller's namespace.

Unless you engage in deep trickery, this is a reliable rule:  a call to a
function cannot change any bindings in the caller's namespace:  if the local
(wrt the caller) name 'x' was bound to an object O before a call, it will
remain bound to O after the call, period.  Now it's possible that O is an
object of a mutable type, in which case the call may *mutate* O, but 'x'
remains bound to (the mutated) O even then.

it's-too-simple-to-fully-grasp-at-first<wink>-ly y'rs  - tim