A question on modification of a list via a function invocation

Ben Bacarisse ben.usenet at bsb.me.uk
Fri Aug 18 10:04:37 EDT 2017


Steve D'Aprano <steve+python at pearwood.info> writes:

> On Thu, 17 Aug 2017 11:37 pm, Ben Bacarisse wrote:
>
>> What goes wrong when someone thinks of Python as passing by value but
>> the value of an expression is an object reference?

This seems to be a hot-button topic so I'd like to try to cool it off a
bit.  To in that spirit I'll point out that I'm not advocating teaching
"the wrong way to think about things", I'm asking if that model,
universally applied, actually leads to misunderstanding Python.

> Lots.
>
> Because even if people *think* about call by value where the value is an
> invisible reference to the actual value, that's not how they talk, because it's
> too hard.

People will drop the word reference (I prefer identity) on occasion but
then they are always going to take shortcuts.

> Look at Scott Stanchfield's extremely influential post. It is *not*
> called:
>
> "Java is call by value where the value is an invisible object reference,
> dammit!"
>
> http://javadude.com/articles/passbyvalue.htm
>
> and consequently people who don't read or understand the *entire* post in full
> take away the lesson that 
>
> "Java is call by value, dammit!"

I don't think this is a fair point.  You will run out of ideas if they
are to be avoided because some people will get the wrong idea when
reading part of a description of that idea applied to some other language.

> So how do we distinguish between languages like Java and Python and
> those like C and Pascal which *genuinely* are call by value, and do
> exhibit call by value semantics? For example, in Pascal, if you
> declare an array parameter without specifying it as "var", the
> compiler will copy the entire array. Java does not do that. Python
> does not do that.

Well, I know how I do that but you are not a fan of that view.

I found it a helpful view because it covers a lot more than just
argument passing by saying something about the set of values that Python
expressions manipulate.  It may be wrong in some case (hence my
question) but I don't think I've been led astray by it (so far!).

> C doesn't treat arrays as first class values, so you can't pass an array as
> argument. You can only pass a pointer to the start of the array. But you can
> declare arbitrarily big structs, and they are copied when you pass them to
> functions. Java objects are not. Despite Scott's insistence that Java is
> exactly the same as C, it isn't.

I'm not here to defend someone else's incorrect statements!  Clearly C
is not the same as Java.  Equally clearly, C only passes arguments by
value (I have no idea about Java).

> If we claim that Python is call by value, without specifying the
> proviso "if you consider the value to be the invisible reference to
> the actual value" every time, we risk misleading others into wrongly
> inferring that Python is "call by value" and you shouldn't pass big
> lists to functions because then they will be copied and that's
> inefficient.

True, though I obviously take issue with using a particularly
long-winded phrase.  See later for how Liskov does it with one short
word.

The core of the idea is actually what the value-set of Python programs
is -- the passing by value just drops out of that.  Talking about
object identities (or references) is not so very cumbersome.  You have
to talk about *something* like that explain the language, don't you?

> Or people will be confused by the behaviour of Python, and decide that maybe it
> is only call by value when you pass an immutable object like a string or int,
> and is call by reference when you pass a mutable object like a list or dict.
> Many people have done that.

I'm not a fan of this notion that an idea is bad because it goes wrong
when people don't understand it.  I don't think any description of
Python's semantics can avoid that trap.

> Imagine this conversation:
>
> * * *
>
> "I was reviewing your code, and in module spam.py I don't understand what value
> the x variable has."
>
> "I don't know. It's something implementation dependent."
>
> "What do you mean?"
>
> "It depends on the interpreter. In CPython the value will be a pointer.
> Something like an address 0x1234abcd."
>
> "But Python doesn't have pointers."
>
> "Okay, call it a reference then. Whatever the implementation uses to point to
> objects."
>
> "I don't care about the Python implementation, I want to know the value of x."
>
> "I told you. It's some unknown and unknowable reference or pointer to an
> object."
>
> "Okay. Given x=1, what's the value of x?"
>
> "How do I know? It depends on the implementation. Something like 0x93a80f16 I
> guess. Why do you care about the value?"
>
>
> * * * 
>
> Obviously this isn't going to happen. Nobody actually thinks like
> this.

No, I should hope not!

> Given
> x=1, we all agree that the value of x is 1, not some invisible, unknown,
> unknowable, implementation-dependent reference.
>
> Instead they have to juggle two mutually contradictory meanings of value in
> their head, and try to guess which one is meant at any point. Sometimes the
> value of x is 1, and sometimes it is the invisible reference, and because that
> is usually implied rather than explicit, there's always room for confusion and
> misunderstanding.
>
> This whole thing is unnecessary. It is no easier to learn that:
>
> "Python is call by value, where the value is an invisible object reference"
>
> than it is to learn that:
>
> "Python is call by object sharing".

I'm not advocating the former because I want to say more than just what
values get passed, and the latter does not explain some other cases that
confuse people new to the language such as

  x = [[1]]
  y = x
  x[0][0] = 42

I find that keeping in mind (despite the odd shortcut I may make) that
all these values really just /refer/ to objects helps me.

> Either way, you have to learn what it means, otherwise it might as well be
> gobbledygook:
>
> "Python is whargle bargle by wazit, where the wazit is fizzbit wacker p'toing".
>
>
> The terminology has been around for forty years. It was invented by
> Barbara Liskov, one of the pioneers of OOP, the same person
> responsible for the Liskov Substitution Principle. So its not like it
> is some obscure term invented by "just some guy on the internet".

Yes I know.  I had the pleasure of talking programming languages with
her once -- scary bright person!  Liskov on CLU:

  x := e
  causes x to refer to the object obtained by evaluating expression e.

Not the "refer".  That's all it takes.  The value of x is not the
object, x /refers/ to the object.  And on calling:

  ... arguments are passed "by object"; the (pointer to the) object
  resulting from evaluating the actual argument expression is assigned
  to the formal. (Thus passing a parameter is just doing an assignment
  to the formal.)

I think this is true of Python too.  If so, I'd be tempted to define
passing "as if by assignment" (as it's done in the C standard) and make
the semantics of assignment the basic feature that needs to be
described.

Finally, from the Python tutorial[1]

  "... arguments are passed using call by value (where the value is
  always an object reference, not the value of the object)."

Maybe I got it from there and generalised a little.  I would not want to
see that remark removed (because, if that's where I got it from, it
helped me), but maybe it is now doomed.

[1] https://docs.python.org/3/tutorial/controlflow.html#defining-functions
-- 
Ben.



More information about the Python-list mailing list