On Nov 4, 2008, at 7:42 AM, Craig Allen wrote:
coming from C/C++ Python seemed to me call by reference, for the
pragmatic reason you said, modificaitons to function arguments do
affect the variable in the caller. The confusing part of this then
comes when immutable objects are passed in.
Yes, you stepped in the wrong direction right away and led yourself
into a morass of contradictions.
The correct way to look at it is that a Python reference is like a C++
pointer. C++ pointers are passed by value (unless you add & to
explicitly make a parameter by-reference), yet you can still use them
to mutate the objects they point to, right? Same thing in Python.
Nothing at all mysterious going on here. Compare this:
typedef Spam* SpamPtr; // (where Spam is some class)
// ...
void foo(SpamPtr spam)
{
spam->count = 4;
}
When you call foo, it modifies the spam object passed in, even though
the parameter is by-value. How? Because (looking more carefully),
you didn't actually pass in a Spam object; you passed in a POINTER TO
a Spam object. That pointer remained unchanged. You just used the
pointer to change some other data living on the heap. This is the
case exactly equivalent to Python:
def foo(spam):
spam.count = 4;
Same thing here; the variable you pass in is a reference to a Spam
object, and while that reference remains unchanged by the call, it is
used to change some other data that lives on the heap.
Here's a C++ example that has no analog in Python, because it uses
call-by-reference:
void throwOut(SpamPtr &spam)
{
printf("throwing out %s\n", spam->brand);
delete spam;
spam = nil;
}
Now here, when you invoke throwOut on a SpamPtr, your own SpamPtr
variable (the one that you pass in) actually gets set to nil. That's
because the formal parameter here is just an alias of the actual
parameter. You can't do that in Python; this attempt:
def throwOut(spam):
print "throwing out %s\n", spam.brand
spam = nil
would entirely fail to have any effect whatsoever on the Spam
reference you pass in. "spam" here is just a local variable within
the throwOut function, which has no connection to the variable passed
in other than it gets a copy of its value (i.e., it initially refers
to the same object as the actual parameter). This doesn't work, and
the C++ throwOut function has no analog in Python, because Python has
no call-by-reference.
Here's another C++ example that has no analog in Python, because it
passes an object directly on the stack rather than a reference to it:
void bar(Spam spam)
{
spam.count = 5;
}
This is the one that I know particularly confuses some users, because
it LOOKS like what you could do in Python, and has the same behavior
on the surface. But it's not analogous at all, because the "spam"
local variable here (and presumably the one in the calling context) is
an object stored directly on the stack, rather than a reference to an
object on the heap. Python can't do that (nor can Java, nor
REALbasic, etc.). This example is also call-by-value, but the value
in this case is a type that has no analog in Python. Python object
variables are references to objects on the heap, just like pointers in
C++ to objects created with "new". So this example is a red herring.
I'd be very interested in hearing whether, as a C/C++ user, the above
explanation is clear and makes sense to you.
Thanks,
- Joe
--
http://mail.python.org/mailman/listinfo/python-list