Finding the instance reference of an object

Dale Roberts gooberts at gmail.com
Mon Oct 27 23:16:04 EDT 2008


[I am actually enjoying this discussion, even though it does not address 
the OP's question. It is helping to solidify *my* understanding.]

Joe Strout wrote:
> On Oct 27, 2008, at 12:19 PM, gooberts at gmail.com wrote:
> 
>> I think this "uncontrived" example addresses the C/Python difference
>> fairly directly (both were tested):
> 
> That's correct, but of course, C is a decades-old language barely a step 
> above assembler.  For a fair comparison, pick any modern OOP language, 
> including the C derivatives (C++, Objective-C, Java), and compare a 
> Python object to an object in that language, rather than to a struct.

Okay, sorry, should have had C++ from the start. See my spiffed-up 
example here, where I magically convert to C++ by replacing "struct" 
with "class" and adding the word "public:". Still behaves the same, though.

I added the functions ByVal() and ByRef() to show that when you pass by 
value, the contents of the object cannot be changed. This is not 
possible in Python unless a copy of the object is made (which is what 
C++ does automatically for you in pass-by-value).

In Python, the contents of the object are changed, which is most similar 
to the pass by reference (or by pointer) construct in C++. But, yes, as 
you say, technically:

   passing the object by reference
        == passing the address of the object by value.

But I think that is, to most programmers, a surprising use of the term 
"pass by value".

And note in my example, nowhere do I explicitly take the address of s1. 
The compiler does this for me in the ByRef() case. And nowhere to I make 
a copy of s1. Again, the compiler does this for me in the ByVal() case. 
The calls to ByVal() and ByRef() are identical. You cannot tell from the 
calls which thing is going to happen.

-----------------------
class MyClass {public: int a;} s1, s2;

void ByVal(MyClass  obj) {obj.a=42;}
void ByRef(MyClass &obj) {obj.a=43;}

int main()
{
    s1.a = 1;
    s2 = s1;

    printf("s1.a %2d   s2.a %2d\n", s1.a, s2.a);

    s1.a = 99;
    printf("s1.a %2d   s2.a %2d\n", s1.a, s2.a);

    ByVal(s1);
    printf("s1.a %2d\n", s1.a);

    ByRef(s1);
    printf("s1.a %2d\n", s1.a);
}
--------------
class mystruct:
     pass

def ByObject(obj): obj.a=42

s1 = mystruct()
s1.a = 1
s2 = s1

print "s1.a %2d   s2.a %2d" % (s1.a,s2.a)
s1.a = 99
print "s1.a %2d   s2.a %2d" % (s1.a,s2.a)

ByObject(s1)
print "s1.a %2d" % (s1.a)

--------------
C++ OUTPUT
s1.a  1   s2.a  1
s1.a 99   s2.a  1  # note s2.a does not change when s1.a is modified
s1.a 99            # contents of s1.a does not change when passed by val
s1.a 43            # it does change when passed by ref

Python OUTPUT
s1.a  1   s2.a  1
s1.a 99   s2.a 99  # s2.a "changes" because it's the same object as s1
s1.a 42            # Contents of object does change with function call.

...and in Python, of course, as you say, what you call the "value" of 
s1, the address of the object, id(val), does not change.

 >>
 >> [skipping lots of stuff we agree on!]
 >>
>> In C/C++, by contrast, variable names correspond directly to memory
>> locations and objects, and you can easily find the addresses of
>> variables.
> 
> Hmm, no, the C++ equivalent of an object reference would be:
> 
>  SomeClass* foo;

Whoah, nonsequitor there. Let's back up. I did not use the word 
"reference", and that is the point. And I am correct that C variable 
names correspond to memory locations (or sometimes CPU registers - 
optimizers can really mangle things). And you can get the addresses of 
the variables themselves using the & operator.

Have a look at my example code again. s1 and s2 ARE OBJECTS THEMSELVES, 
they are not references to objects. You can do this in C++ (not in 
Python). You can pass whole objects, by value (they are copied by the 
compiler), on the stack. And we both understand that you can't do that 
in Python. That is why we differentiate between "pass by reference" and 
"pass by value" in C++, and people generally understand what that means.

 > ...
>  void NiftyMethod2(SomeClassPtr &arg) {...}
>  ...
>  NiftyMethod2(foo);
> 
> Now, arg here is passed by reference (just like a ByRef parameter in RB 
> or .NET).  That means that NiftyMethod2 could very well change the value 
> that is passed in.  It could actually make foo point to something else.
> 
> No Python method can do that,

Yes, absolutely agreed. A Python method can never change *which* object 
the caller points to, it can only change the *contents* of that object.

But the same is true in C++ as well. In your example, the address of the 
foo variable itself can never be changed. You can change the *value* of 
foo (so it points to a different object), or you can change the contents 
of the object foo points to. foo is a variable with an address which you 
can usually see with a printf("%x", &foo), and that is different from 
the address of the object which you get when you say printf("%x", foo).

The value &foo cannot be changed.

> because Python arguments are ALWAYS passed 
> by value.  There is no call by reference in Python.  Period, end of 
> story, nothing to see here.

Yea, BUT... If you tell this to a C++ programer without any further 
explanation, they will be thoroughly confused and misinformed, unless 
you point them to this thread or amend that statement with your version 
of what "pass by value" means. I know what you mean, and you know what I 
mean, but that's because we've both read through this thread ;-)

Look at my example. The ByVal() routine behaves how C++ programmers 
expect "pass by value" to work. The contents of the caller's object 
cannot be modified.

So, then, what to tell a C++ programmer about how Python passes 
arguments? You say: tell them Python only passes by value. I disagree, 
because I think that would confuse them. Rather than try to map C++ 
conventions onto Python, I think it is more useful to just tell them how 
it really works. Maybe a few statements like this:

   All values in Python are objects, from simple integers up to complex
   user-defined classes.

   An assignment in Python binds a variable name to an object. The
   internal "value" of the variable is the memory address of an object,
   and can be seen with id(var), but is rarely needed in practice.

   The "value" that gets passed in a Python function call is the address
   of an object (the id()).

   When making a function call, myfunc(var), the value of id(var) can
   never be changed by the function.

Not sure if these are the best. To get into much more detail, you have 
to start explaining mutable and immutable objects and such.

dale


> Cheers,
> - Joe



More information about the Python-list mailing list