[Tutor] Still confused about Python references/objects

Tim Peters tim.one@home.com
Sat, 31 Mar 2001 22:51:39 -0500


[Sheila King, on trying to understand Python via reference semantics]
> You're telling me! (I bring background experience from a few different
> languages: Pascal, Fortran, VB, C++, and they ALL behaved that
> way! It is so difficult to get used to this new way of thinking.)

If it's any consolation, those to whom Python is a first language have no
trouble at all with it, and probably suffer even greater trying to keep track
of the new mysteries that pop up when they move to other languages.

The eff-bot (Fredrik Lundh) wrote a very nice and tiny intro, here:

    http://w1.132.telia.com/~u13208596/guides/python-objects.htm

Check it out!  The first instruction is vital, though:  "Reset your brain.".
Once you do, "the rules" are short and sweet (his writeup is less than one
page of text, including examples).

> ...
> OK, so I think I get what you're saying. It sounds like, in order to
> have a function modify an object in the calling functions space, either
> it has to be a mutable object, or else I would (I think?) have to
> return the new version I want, and assign it to a name in the calling
> function's namespace ???

Yup!  Note too that module-global objects are visible from *all* function and
method namespaces, so that's another possibility:

def f():
    global x
    x = 42
    y = 42
    g(y)
    print x  # happens to prints 666
    print y  # prints 42, and no matter *what* g() does

def g(a):
    global x
    x = 666

f()

But heavy use of globals is bad practice, in part precisely *because* any
call can change global bindings at any time.  You can see this in the body of
f:  you can't possibly guess what "print x" is going to do without tracking
down and studying the source code for g, but you can be certain that "print
y" will print 42 without looking at g.  This makes the use of y much easier
to understand in isolation, and the larger your programs get, the more
valuable it is to limit the amount of code you need to examine.  Local names
and immutable objects can both help with that.

> ...
> So, now I think I know what I'm doing, and I try this:
>
> >>> def swap(two_tuple):
> 	x,y = two_tuple
> 	return y, x
>
> >>> a = 2
> >>> b = 3
> >>> print a, " ", b
> 2   3
> >>> a,b = swap((a,b))
> >>> print a, " ", b
> 3   2
> >>>

Little-known feature:  just like you can do

    x, y = two_tuple

and Python will "magically" unpack the two_tuple into x and y, you can also
do that in formal argument lists.  So

def swap((x, y)):
    return y, x

also works.

> OK, I think I got it. The only way to think of a function as modifying
> something in the calling namespace, is either if it is a mutable
> object, or else it has to return the new object that I want to
> reference.

Yes, and because (1) the called function simply has no access to its caller's
namespace, and (2) there is no pointer type in Python, so you *can't* pass "a
reference" to an object (btw, I think *that's* the fundamental reason why
trying to think about Python in "call by reference" terms ends up confusing
people:  Python simply doesn't have references in that sense; I call it "call
by object" to force people to break out of inappropriate mental models).

you're-well-on-the-road-to-a-full-recovery<wink>-ly y'rs  - tim