Official definition of call-by-value (Re: Finding the instance reference...)

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Sun Nov 16 07:06:20 CET 2008


On Sat, 15 Nov 2008 11:17:07 -0800, rurpy wrote:

>>> * Can I create an object that has a value that
>>>  is the same as int(3) without somehow using an int(3) object in its
>>>  construction?
[...]
>> Yes: mpz(3) where mpz is multi-precision int class with same set of
>> possible values as Python ints.
> 
> The argument to mpz() is int(3).

Does mpz take string arguments? If it doesn't, it is easy to imagine a 
version that does.

mpz("3")



>>> * Do all objects have values? (Ignore the Python
>>>  docs if necessary.)
>>
>> If one allows null values, I am current thinking yes.
> 
> I don't see a difference between a "null value" and not having a value.

Ah, the Medieval concept of numbers. Of course it's very much older than 
Medieval. Roman and Greek mathematics was bedeviled by their philosophy 
that one is the smallest number. (In fact, some early mathematicians 
argued that *two* was the smallest number, for the reason that if you 
have only one sheep (say) it would be unnatural to say that "I have a 
number of sheep") It wasn't until the fifth century C.E. that Indian 
mathematicians invented the concept of zero, and it took many centuries 
for the idea to get to Europe via the Arabs.

If you wish to say that null values aren't values at all, you will find 
that you have many conceptual difficulties. Given:

x = 5
y = 5
z = x - y

you will be forced to say that x and y have values but z does not. But 
that doesn't mean that z is undefined -- it means that z is defined and 
has no value, which makes communication very much more complicated. You 
are forced to say things like:

"The value of the object is the number of sheep in the paddock, unless 
the number of sheep is zero, in which case the object has no value..."

which is needlessly complicated.

I say that 0 is a perfectly fine value. So is None, [], {}, and any other 
null-value. I recommend you don't complicate and confuse matters by 
trying to treat them differently.


 
>> Still, numbers, characters, and collections thereof of what code is
>> usually about.
> 
> Yes, which is why we have int, str, and list classes which, unlike some
> others, can have values.

What classes do you think have no values?


 
[snip]
>>> * Does an object's behavior (methods) affect
>>>  its value?
>>
>> My first answer is No.  Instance methods are attributes of a class and,
>> in most cases, the value of a class.
> 
> The value of a class is it's attributes? Are you saying that attributes
> of an object are part of its value?  That would mean that 'a' and b'
> below have different values?
> 
> class My_int(int):
>     def __init__(self): self.foo = None

That won't work you know.

> a = int(3)
> b = My_int(3)


That depends on whether the existence of foo makes a difference to you or 
not. Consider pickle. Since pickle can't predict what aspects of the 
object are important, it must treat *everything* as significant, and 
pickle will absolutely treat a and b as having different values.

(Actually, that's not quite true: by necessity pickle *cannot* treat 
identity as important. If your application requires object identity to be 
persistent over execution session, you can't do so in Python. Hence 
pickle and related serialisers can afford to ignore identity.)

Given input of b, the round-trip of pickle/unpickle must return an object 
with a foo attribute. If the round-trip merely returns a My_int instance 
without the foo attribute, we would rightly consider it a bug, that the 
output doesn't have the same value as the input. Likewise if it returned 
int(3) instead of My_int(3).

But other functions may have weaker constraints. Consider sum([a, b]). 
The function sum makes no promises that it will return the same type as 
it's arguments. Since, *for the purposes of addition*, the foo attribute 
has no significance, sum() makes no promise whether the sum of a and b 
will include the foo attribute. In fact it does not. As far as addition 
is concerned, a and b have the same value, and the foo attribute is lost.

In general, Python makes the fewest possible promises of that nature. If 
you want to treat foo as having a significant part of the value of b, 
then you need to deal with it yourself, perhaps by writing __add__ and 
__radd__ methods to My_int.

But from a philosophical position, as opposed to a practical one, of 
course a and b have different values. The value of a is (the integer 3), 
and the value of b is (the integer 3 together with an attribute foo), 
even if foo is a mere decoration, a stripe of red paint on an otherwise 
identical object. Since they are different, the objects a and b have 
different values. This is a valid philosophical position, although in 
practice we often lower our expectations, especially when duck-typing. We 
don't care about the red stripe or not, and so we are indifferent to 
whether we get int(3) or My_int(3).


 
> I propose that attributes are not part of a class' (or any other
> object's) value and that a class object has no value.

Consider:

import urllib2
x = urllib2.urlopen('http://www.yahoo.com')
y = urllib2.urlopen('ftp://ftp.dina.kvl.dk/pub/Math-reports/README')

Do you really want to say that the objects x and y have the same value 
(or worse, "no value") just because all the significant "stuff" that 
distinguishes x from y are stored as attributes? I suggest that you are 
looking at many conceptual difficulties if you make that distinction.

I would say that the value of the object x is the open HTTP connection to 
www.yahoo.com while the value of the object y is the open FTP connection 
to ftp.dina.kvl.dk/pub/Math-reports/README. The nature of how those 
values are stored is irrelevant. Whether they are class attributes in the 
Python implementation, or data structures in C, or byte patterns in a 
database, is irrelevant (except perhaps for performance issues).



>>> * If two objects have different unrelated types
>>>  (i.e. not subclassed), are their values necessarily different
>>>  (regardless of how they print, or act when used in an expression)?
>>
>> No.  See above Q. about int(3).
> 
> I now think this answer is yes.  See above Q. about int(3). :-)
> 
>>> * What if they aren't unrelated but one is a sub-
>>>  class of the other?
>>
>> Easier no than above.
> 
> Yes, I agree no. :-)

Classes and subclasses is a mere implementation detail, a specific method 
of implementing a range of useful programming techniques. Consider 
delegation as an alternative to inheritance.

class MyInt:
    def __init__(self, value):
        self.__dict__['_delegate'] = value
    def __getattr__(self, name):
        return getattr(self._delegate, name)
    def __setattr__(self, name, value):
        setattr(self._delegate, name, value)
    def __delattr__(self, name):
        delattr(self._delegate, name, value)

n = MyInt(3)

The object n behaves just like int(3), except the class is different. For 
the purposes of arithmetic, why would you insist that their values are 
*necessarily* different?


Consider this subclass of int:

class WeirdInt(int):
    def __new__(cls, value):
        return int.__new__(cls, -value)
    def __init__(self, value):
        self._magic = value
    def __str__(self):
        return str(self._magic)
    __repr__ = __str__

n = WeirdInt(3)

Why would you say that the value of n is *necessarily* the same as the 
value of int(3) just because it is subclassed from int? Let's see how 
they behave:

>>> 3 + 1
4
>>> n + 1
-2
>>> n
3


I would say that n has a unique value: it is an object that looks like 3 
but behaves like -3. It is different from both int(3) and int(-3). 
Subclassing is irrelevant.



>>> * Can one completely hide an object's value by
>>>  overriding methods?
>>
>> You tell me ;-)
> 
> My guess is no.  For example, if I try to make int that appears to be 1
> + its "real" value:

[snip failed example]

You neglected to override __float__.

But using the example of WeirdInt above:

>>> n + 1.0
-2.0



>> Do you have an example that you were thinking of?
> 
> Nothing specific.  I was trying to see if one can separate the behavior
> of objects from whatever it is that constitutes value. The example
> above conveys to me at least that value and behavior *can* be separated
> -- even if one could completely hide the "real" value of an object, one
> can still view it as having a real value, and it's apparent value as
> something produced by it's behavior.

Ah, you're coming from the Platonic view of "Ideal Forms". 
http://en.wikipedia.org/wiki/The_Forms

Aristotle was a critic of that, and I don't think modern philosophers 
think too highly of it either. In any case, it's rather impractical to 
use as a basis for understanding a programming language.



> Here is my (tentative) concept of value.  I will give a wordy version
> and leave a clear, concise definition to later, or someone else,
> whichever occurs first. :-)
> 
> Some builtin[1] objects have a value. A value is a "hidden" piece of
> data in an object that is not directly accessible from the Python
> language; access to an object's value is provided only through methods
> of the object's class.  The object's methods can (and usually will) make
> use of the object's value when producing an object to return or changing
> the object's state or value.

I see you are still insisting that value is something that objects "have" 
rather than "are". I don't see that this is a useful stance to take, 
except in the sense of a comparison to some sort of Platonic Ideal or 
abstract concept, e.g. the object int(3) has the value of the abstract 
whole number three. That's generally not very helpful: in practice, we 
can assume that objects are just like the abstract values, except when 
they're not. 

(E.g. 1+1 in Python is just like 1+1 in pure mathematics; however 
10**10**100 in Python is significantly different from 10**10**100 in pure 
maths. For starters, the calculation in Python will probably take longer 
than the expected lifespan of the Universe; the calculation in pure maths 
takes as long as it takes the mathematician to write down "one 
googolplex".)

Working around those leaky abstractions is an important part of 
programming, but it doesn't help much trying to understand the principles 
of a language.


> It is not possible to define a class in Python that provides its objects
> with a value unless that class is derived from a builtin class that
> provides a value.[3]

Again, I do not believe that this is a helpful approach to take. Why 
should the value (or lack thereof) of an object depend on the 
implementation details of how it is created?


 
> If this in not an inaccurate description of "value" it clears up several
> points of confusion for me: 

> * No need to worry about what the value of object() is.
>   (it has no value.)

I don't believe that this is a question that needs any worry. The value 
of object() is simply the instance itself. 



> * No need to worry about whether expressions return
>   values.  (They don't, they always return (references to) objects.)

An expression is a symbol. It's a compound symbol, and potentially large 
and complicated, but still a symbol. The term "value" has a perfectly 
good definition:

That which a symbol denotes or represents.

Given the expression:

Whatever(57) if y or SomeObject(4, 5, "foo") else SomethingDifferent("x")

we can take the entire expression as a symbolic representation of a 
thing. It doesn't matter whether we can determine what that thing is at 
compile-time or run-time. We know that at run-time there will be such a 
thing, and that thing is the value of the expression. (The sole exception 
is if the expression fails to evaluate at all.)

So, if the expression does evaluate, then it has evaluates to a thing. It 
represents that thing: when talking or reasoning about the thing, we can 
substitute the expression for that thing. Doing so might be counter-
productive, because the expression is so long and complicated. But we can 
create a *short* symbol representing the expression, say x, and now talk 
about x as a short-hand for the long complicated expression. Going back 
to the dictionary meaning of "value", we can say that the value of x is 
whatever thing is represented by it, which is the same thing as 
represented by the expression, which is whatever object the expression 
evaluates to.

In other words: the value of an expression is the object that the 
expression evaluates to.



> * Where do values come from? (They are created/modified
>   by methods of builtin classes and are otherwise not directly
>   accessible.)

There are many things of interest which are not created/modified by 
methods of built-in classes, and it would be terribly limiting to say 
that they don't have values.

I might choose to implement integers as follows:

class UnaryInt:
    def __init__(self, n):
        if n == 0:
            self.ptr = None
        else:
            self.ptr = UnaryInt(n-1)
    def __add__(self, other):
        result = UnaryInt(other)
        # Find the end of the linked list.
        obj = result
        while obj.ptr is not None:
            obj = obj.ptr
        # And extend it.
        obj.ptr = self.ptr
        return result
    __radd__ = __add__
    def __str__(self):
        count = 0
        obj = self
        while obj.ptr is not None:
            count += 1
            obj = obj.ptr
        return "%s" % count
    __repr__ = __str__


This is an incomplete class -- I haven't spent the time to implement all 
the methods necessary to make it work correctly, so it is *very* easy to 
break. For example, don't pass a negative integer to it. I could get that 
to work too, but it would require significant effort. But for the simple 
case of this demonstration, it is good enough.

According to your definition, UnaryInt(3) has no value. But I argue that 
it is merely a different implementation of int(3):

>>> n = UnaryInt(3)
>>> n + 4
7


Why should I not say that the value of UnaryInt(3) is three, just like 
the value of int(3) is three? The differences are mere implementation 
details.



> * How can I find an object's value (if I don't believe
>   .str(), .repr(), etc)?  Use gdb. :-)

I would say the object's value is the value, so if you have the object, 
you have its value. 

What your question really is, how can I be sure that the string 
representation of an object tells me everything I want/need to know about 
the object? And the answer is, naturally, you can't.

Python has very powerful introspection tools:

type()
str(), repr()
help()
dir()
the inspect module

and probably others. If you want to know what makes a thing (a Python 
object) itself instead of another, different, thing, then use them.



-- 
Steven



More information about the Python-list mailing list