Is 'everything' a refrence or isn't it?

M

Mike Meyer

Steven D'Aprano said:
Mike said:
Steven D'Aprano said:
so if I code this:
lst = [1,2,3]
for i in lst:
if i==2:
i = 4
print lst
I though the contents of lst would be modified.. (After reading that
'everything' is a refrence.)
See, this confusion is precisely why I get the urge to slap people who
describe Python as "call by reference". It isn't.
Except this doesn't have *anything at all* to do with python being
(or
not being) call by reference. This is a confusion about name binding
vs. assignment to a variable. The proper people to slap around for
this case are the ones who talk about assignment to a variable.
Mike, you are wrong. Not about talk about "assigning to variables"
being harmful, I agree with that. But this confusion is *exactly*
about the call by reference misunderstanding. How do I know this?

Because the Original Poster said so! He said, to paraphrase, "Hey, I
thought Python was call by reference, but I tried this, and it didn't
work, what gives???"

And he's right, and you're wrong. Look at the *code*. There isn't a
single call in it. He may have said "call by reference", but his
example code didn't have anything to do with call by rereference, and
he didn't demonstrate any confusion about the semantics of parameter
passing. He *did* demonstrate confusion about the semantics of the
assignment statement.
Who cares what Python does internally? It is strictly irrelevant
whether Python internally passes around pointers, or copies values, or
sometimes one or the other.

Who said anything about what Python does internally? I agree, it's
strictly irrelevant. What matters is the *behavior*.
It is not a question of whether Python's behaviour is *sometimes* the
same as call by reference. The question whether Python's behaviour is
*always* the same as CBR, and it is not.

You're wrong. Python's behavior is *always* the same as CBR, once you
identify what Python is passing a reference to.
Likewise, you shouldn't argue that Python is obviously call by value
just because *sometimes* it has the same behaviour as CBV.

Actually, it's either always the same as CBV, or never the same as
CBV. It's always the same as CBV because you always pass a value - and
the value is always a reference. But every calling mechanism requires
that you always pass a value. It's what the value is that determines
the calling mechanism. You pass a reference for CBR, and a name for
call by name.
I mean, come on! A whole bunch of people argue black and blue that
Python "obviously" is call by reference, and another vocal bunch argue
that it is "obviously" call by value.

I don't think I've seen anyone arguing that Python is
call-by-value. There are people who argue that it's sometimes one and
soemtimes the other, but that's mostly because they have incorrectly
identified what Python is passing a reference to.
Just google on "Python is call by value" and "Python is call by
reference" -- including quotes -- to see this argument come up time
and time again, year after year after year. And all because of some
false dichotomy that CBR and CBV are the only two valid descriptions
of a computer language.

I tried the google, and I don't see a lot of arguments. I see people
declaiming one way or another, and not very many of those. I don't
think it's an apparent dichotomy has much to do with it. I know there
are other things than CBV and CBR, and even understand Jensen's
device.

<mike
 
S

Stuart D. Gathman

I was under the assumption that everything in python was a refrence...

so if I code this:
lst = [1,2,3]
for i in lst:
if i==2:
i = 4
print lst

I though the contents of lst would be modified.. (After reading that
'everything' is a refrence.)
...
Have I misunderstood something?

It might help to do a translation to equivalent C:

int _i1 = 1;
int _i2 = 2;
int _i3 = 3;
int _i4 = 4;
int* lst[NLST] = { &_i1,&_i2,&_i3 };
int _idx; /* internal iterator */
for (_idx = 0; _idx < NLST; ++_idx) {
int *i = lst[_idx];
if (*i == *_i2)
i = &_i4;
}
for (_idx = 0; _idx < NLST; ++_idx)
printf("%d\n",*lst[_idx]);
 
M

Mike Meyer

Steven D'Aprano said:
Nonsense. What is stored in the list is an object. Python doesn't have
pointers. You are confusing the underlying C implementation, which
implements lists as an array of pointers, with the high-level Python
code, which doesn't even know what a pointer is. It only has objects.

I didn't say a single word about C, or pointers, or anything else to
do with the implementation.
Since we are discussing the perspective from a Python
programmer's point of view, it is irrelevant what the C implementation
does. If lists held references, you could do this:

x = 5
L = [1, 2, x]
# the third item points to the same memory location
# as x; changing it will also change x
L[2] = 7
assert x == 7

which is the behaviour that "call by reference" implies. But it
doesn't work that way in high-level Python code.

Now you're talking nonsense. Call by reference is about *parameter
passing*, not about the behavior of assignment statements or
lists. How the above code behaves has *nothing* to do with how Python
passes parameters.
The *reason* it doesn't work is that the object 5 is immutable.

The immutability of 5 has *nothing* to do with it. If you replace 5
and 7 with mutable objects, you get the exact same behavior:

x = [5]
L = [1, 2, x]
L[2] = [7]
assert x == set([7])

You seem to be suffering from the newbie confusion of thinking that a
Python assignment statement writes a value into the lvalue. It doesn't
- it makes the lvalue a reference to the object the right side
evaluates to ("binds a name to a value", except sometimes the lvalue
isn't a name). I'm pretty sure you know the difference.

The same thing applies to CBR/CBV. The immutability of the objects
being passed ain't got nothing to do with it. It's the fact that
assignment statements bind values instead of write them into a
variable that makes Python's CBR behavior sometimes act like CBV.
If you only ever passed mutable objects around, you would think
Python was call by reference.

True. But you'd still run into things that you can do with CBR in
other languages that you can't do in Python. That's not because Python
isn't CBR, it's because those other languages have things that Python
doens't have.
And if you only ever passed immutable objects around, you would
think Python was call by value.

You might. Then again, you might also understand the concepts well
enough to realize that there isn't any difference between CBR and CBV
when you're passing immutable objects.
But since Python has both sorts of objects, it displays both sorts
of behaviour.

Again, the mutability (or lack thereof) of the objects being passed
doesn't have anything to do with it. The things you can do in other
languages using CBR that you can't do in Python - like changing the
binding of a name in the callers namespace - you can't do whether the
name is bound to a mutable object or an immutable object. That Python
looks like CBV when you pass immutable objects is because CBR looks
like CBV when you pass immutable objects.

<mike
 
B

Bryan Olson

Mike said:
Steven D'Aprano writes: [...]
Because the Original Poster said so! He said, to paraphrase, "Hey, I
thought Python was call by reference, but I tried this, and it didn't
work, what gives???"


And he's right, and you're wrong. Look at the *code*. There isn't a
single call in it. He may have said "call by reference"

Actually he didn't. Steven added "call by" in his paraphrase.
 
D

Donn Cave

Quoth Steven D'Aprano <[email protected]>:
| Mike Meyer wrote:
[... dragging out his own terminology hangup ]
|> Except this doesn't have *anything at all* to do with python being (or
|> not being) call by reference. This is a confusion about name binding
|> vs. assignment to a variable. The proper people to slap around for
|> this case are the ones who talk about assignment to a variable.
|
| Mike, you are wrong. Not about talk about "assigning to
| variables" being harmful, I agree with that. But this
| confusion is *exactly* about the call by reference
| misunderstanding. How do I know this?
|
| Because the Original Poster said so! He said, to
| paraphrase, "Hey, I thought Python was call by
| reference, but I tried this, and it didn't work, what
| gives???"

But he really did not SAY "call by reference"! The actual quote:
"I was under the assumption that everything in python was a refrence..."
and
"(After reading that 'everything' is a refrence.)"

Just give it up. And you too, Mike! "variable" is the commonplace
term for the things we fling around in our programs, and you have to
accept that we will use that word whether we're talking about Python,
Java, C or what have you. Of course the semantics of variables - how
they actually relate to values - may be radically different from one
language to another, but it universally plays essentially the same role
in programming (or mathematics), a thing whose value may vary according
to the logic of the program.

C and FORTRAN don't own this word, and it isn't just their version
against the way Python and some other languages do it. Each language
has its own angle on it, and they're all going to be called variables.

Donn Cave, (e-mail address removed)
 
C

Claudio Grondi

Stuart said:
I was under the assumption that everything in python was a refrence...

so if I code this:
lst = [1,2,3]
for i in lst:
if i==2:
i = 4
print lst

I though the contents of lst would be modified.. (After reading that
'everything' is a refrence.)
...
Have I misunderstood something?


It might help to do a translation to equivalent C:

int _i1 = 1;
int _i2 = 2;
int _i3 = 3;
int _i4 = 4;
int* lst[NLST] = { &_i1,&_i2,&_i3 };
int _idx; /* internal iterator */
for (_idx = 0; _idx < NLST; ++_idx) {
int *i = lst[_idx];
if (*i == *_i2)
^-- I have trouble with this line. Is it as is should be? I suppose
it is not.

Claudio
i = &_i4;
}
for (_idx = 0; _idx < NLST; ++_idx)
printf("%d\n",*lst[_idx]);
 
B

Ben Sizer

Steven said:
On reading back over my post, I realise that it might
sound like I was mad at KraftDiner. My apologies -- I'm
not, I feel (s)he is the victim of incorrect
information here, not the culprit.

After all, as a Python newbie, how is KraftDiner
supposed to know that when people say "Python is call
by reference", what they actually mean is "Um, except
that it doesn't behave like any call by reference
language you're likely to have used before, and
sometimes it behaves more like call by value"?

But, if you separate the calling mechanism from the assignment
mechanism, then Python does behave like every other call by reference
language. The problem is that people expect to then be able to change
the value of the referred object with the assignment operator. It's the
assignment semantics that differ from languages such as C++ and Java,
not the calling mechanism. In C++, assignment means copying a value. In
Python, assignment means reassigning a reference.

The unfortunate problem is that people think of 'pass by reference' in
association with 'assignment is a mutating operation', when really the
2 concepts are orthogonal and Python replaces the second with
'assignment is a reference reseating operation'. The only reason I
stress this is because with this in mind, Python is consistent, as
opposed to seeming to have 2 different modes of operation.
 
D

Dan Sommers

I was under the assumption that everything in python was a refrence...

so if I code this:
lst = [1,2,3]
for i in lst:
if i==2:
i = 4
print lst

I though the contents of lst would be modified.. (After reading that
'everything' is a refrence.)
...
Have I misunderstood something?
It might help to do a translation to equivalent C:
int _i1 = 1;
int _i2 = 2;
int _i3 = 3;
int _i4 = 4;
int* lst[NLST] = { &_i1,&_i2,&_i3 };

Okay so far.
int _idx; /* internal iterator */
for (_idx = 0; _idx < NLST; ++_idx) {
int *i = lst[_idx];

[snip]

That's the C idiom

for i in range(len(lst))

we all complain about here. How about (untested):

/*
iterate over the list, binding i to each item in succession;
_idx is internal to the interpreter;
separate the definition of i from the assignment of i for clarity
*/

int **_idx;
for( _idx = lst; _idx < lst + NLST; ++_idx ) {
int *i;
i = *_idx;

/* compare "the item to which i is bound" to "a constant" */
if( *i == *(&_i2) )
/* rebind i to _i4 */
i = &_i4;
}
for (_idx = 0; _idx < NLST; ++_idx)
printf("%d\n",*lst[_idx]);

for( _idx = lst; _idx < lst + NLST; ++_idx ) {
int *i = *_idx;
printf( "%d\n", *i );
}

Regards,
Dan
 
D

David Murmann

Claudio said:
Stuart said:
for (_idx = 0; _idx < NLST; ++_idx) {
int *i = lst[_idx];
if (*i == *_i2)
^-- I have trouble with this line. Is it as is should be? I suppose
it is not.

i think he meant

if (*i == _i2)

but i think python does

if (i == &_i2)

because there is only one integer object holding the value 2, so
it is sufficient to compare the addresses (i'm not sure about this,
perhaps someone smarter can clarify?).
 
D

David Murmann

Ich said:
but i think python does

if (i == &_i2)

because there is only one integer object holding the value 2, so
it is sufficient to compare the addresses (i'm not sure about this,
perhaps someone smarter can clarify?).

well, as far as i can see the relevant function is

static int
int_compare(PyIntObject *v, PyIntObject *w)
{
register long i = v->ob_ival;
register long j = w->ob_ival;
return (i < j) ? -1 : (i > j) ? 1 : 0;
}

in Objects/intobject.c, which does compare by value. so, this is
either special-cased elsewhere or not optimized (should/can it be?).
 
C

Claudio Grondi

Dan said:
I was under the assumption that everything in python was a refrence...

so if I code this:
lst = [1,2,3]
for i in lst:
if i==2:
i = 4
print lst

I though the contents of lst would be modified.. (After reading that
'everything' is a refrence.)
...
Have I misunderstood something?

It might help to do a translation to equivalent C:

int _i1 = 1;
int _i2 = 2;
int _i3 = 3;
int _i4 = 4;
int* lst[NLST] = { &_i1,&_i2,&_i3 };


Okay so far.

int _idx; /* internal iterator */
for (_idx = 0; _idx < NLST; ++_idx) {
int *i = lst[_idx];


[snip]

That's the C idiom

for i in range(len(lst))

we all complain about here. How about (untested):

/*
iterate over the list, binding i to each item in succession;
_idx is internal to the interpreter;
separate the definition of i from the assignment of i for clarity
*/

int **_idx;
for( _idx = lst; _idx < lst + NLST; ++_idx ) {
int *i;
i = *_idx;

/* compare "the item to which i is bound" to "a constant" */
if( *i == *(&_i2) )
/* rebind i to _i4 */
i = &_i4;
}

for (_idx = 0; _idx < NLST; ++_idx)
printf("%d\n",*lst[_idx]);


for( _idx = lst; _idx < lst + NLST; ++_idx ) {
int *i = *_idx;
printf( "%d\n", *i );
}

Regards,
Dan
This code appears to me better than that original one, but it exceeds at
the moment my capacity to check it. Weren't it a good idea to use PyPy
to translate this Python code into C? It should exactly show how it
works, right?
I would be glad to hear how to accomplish this, because I have heard, it
is not easy to separate and use that part of PyPy which does the
translation.

Claudio
 
D

David Murmann

Ich said:
well, as far as i can see the relevant function is
in Objects/intobject.c, which does compare by value. so, this is
either special-cased elsewhere or not optimized (should/can it be?).

it is special-cased, but still compares by value. the relevant
parts from "Python/ceval.c":

case COMPARE_OP:
if (PyInt_CheckExact(w) && PyInt_CheckExact(v)) {
a = PyInt_AS_LONG(v);
b = PyInt_AS_LONG(w);
switch (oparg) {
case PyCmp_EQ: res = a == b; break;
}
}

sorry if i am being noisy.
 
D

Duncan Booth

David said:
but i think python does

if (i == &_i2)

because there is only one integer object holding the value 2, so
it is sufficient to compare the addresses (i'm not sure about this,
perhaps someone smarter can clarify?).

No, Python itself only allocates one integer object holding the value 2,
but that isn't necessarily the only object which can compare equal to 2,
nor even the only integer object with the value of 2 (a C extension could
create another).

I seem to remember that at one time Python tried to 'optimise' the
comparison by saying that if the addresses are equal the objects are equal,
and if the addresses are not equal call the __eq__ method, but this doesn't
work in general as it is possible for an object to not be equal to itself.
(e.g. a NaN or a user defined class).

A suitable short equivalent for the comparison is:

if (*i == *(&_i2)) ...

but a closer equivalent of what Python really does might be:

object *_tmp = &_i2;
if ((PyInt_CheckExact(i) && PyInt_CheckExact(_tmp)) ?
*i==*_tmp : PyObject_RichCompare(i, _tmp, PyCmp_EQ)) ...

i.e. check the types (not that you could actually do that with the C
translation of the code) for two integers which fit in the range of C longs
and compare the values if possible, otherwise call some horrendously
complex comparison code.
 
D

David Murmann

Dan said:
int **_idx;
for( _idx = lst; _idx < lst + NLST; ++_idx ) {
int *i;
i = *_idx;

/* compare "the item to which i is bound" to "a constant" */
if( *i == *(&_i2) )
/* rebind i to _i4 */
i = &_i4;
}

for( _idx = lst; _idx < lst + NLST; ++_idx ) {
int *i = *_idx;
printf( "%d\n", *i );
}

i don't see what is so fundamentally different in this version compared
to the one from Stuart Gathman (except it works, but i think it was a typo).
do you want to stress that python uses an iterator (if so, i would have
renamed _idx to _iter, because it ain't not index anymore)? whats the point
of not starting the for loop at 0? and is there a difference between
(*i == _idx) and (*i == *(&_idx))?
 
M

Mike Meyer

Ben Sizer said:
But, if you separate the calling mechanism from the assignment
mechanism, then Python does behave like every other call by reference
language. The problem is that people expect to then be able to change
the value of the referred object with the assignment operator. It's the
assignment semantics that differ from languages such as C++ and Java,
not the calling mechanism. In C++, assignment means copying a value. In
Python, assignment means reassigning a reference.

The unfortunate problem is that people think of 'pass by reference' in
association with 'assignment is a mutating operation', when really the
2 concepts are orthogonal and Python replaces the second with
'assignment is a reference reseating operation'. The only reason I
stress this is because with this in mind, Python is consistent, as
opposed to seeming to have 2 different modes of operation.

Exactly. The critical information is that python's assignment
statement is different from what people are used to from C/C++/etc.
While telling them that "You can't do call by reference because Python
is call by object" may be correct, it leaves out the critical
information. So they're liable to run into the same problem in a
different context - like the one the OP had.

<mike
 
D

Dan Sommers

For reference, Stuart Gathman's looks like this:
int _idx; /* internal iterator */
for (_idx = 0; _idx < NLST; ++_idx) {
int *i = lst[_idx];
i don't see what is so fundamentally different in this version
compared to the one from Stuart Gathman (except it works, but i think
it was a typo) ...

I agree that Stuart's error was a typo.
... do you want to stress that python uses an iterator (if so, i would
have renamed _idx to _iter, because it ain't not index anymore)? whats
the point of not starting the for loop at 0? ...

I was trying to show iteration over a list of items, rather than indexed
access into a list of pointers, but it is no longer as clear to me as it
was at the time that I did a good job of that. ;-)

Another thing I wanted to show is that no amount of changing i would
have any effect on the list, and I missed it in Stuart's code (my
fault).

Or maybe my Python/C ratio is so high that seeing a loop variable used
as an index into a list or an array just bugs me. ;-)

_iter vs. _idx is a non-starter for me because of the baggage associated
with those words.
... and is there a difference between (*i == _idx) and (*i ==
*(&_idx))?

Again, just the emphasis that the comparison is through bindings rather
than directly on the objects, to show that the bindings are completely
independent of the objects. I am pretty sure that there is no semantic
difference between *(&_idx) and _idx in the "normal" case, but there may
be some very odd corner cases if _idx were volatile and/or delcared
register.

Regards,
Dan
 
B

Bengt Richter

I was under the assumption that everything in python was a refrence...

so if I code this:
lst = [1,2,3]
for i in lst:
if i==2:
i = 4
print lst

I though the contents of lst would be modified.. (After reading that
'everything' is a refrence.)
so it seems that in order to do this I need to code it like:

lst = [1,2,3]
for i in range(len(lst)):
if lst == 2:
lst=4
print lst

Have I misunderstood something?

Evidently, as others have already elaborated upon ;-)

I am wondering if it might be helpful to focus on expressions
first, and point out that the result is always a reference to
an object representing the value of the expression.

Since you always get a reference to a value-representing object,
assignment can only have meaning in terms of what you can do with that reference.
You can never assign a value per se. Therefore an assignment is
placing the reference in a place determined by the assignment target syntax.

The simplest is a bare name, and then we speak of binding a name to the
value-representing object. The name belongs to some context, and name space,
and an implementation will determine how to find the named binding site that
will hold the reference, depending on what name space is involved. E.g. the
binding site might be an optimized slot in a function's local name space.

Other assignment syntaxes also create bindings, and also identify what I am
calling binding sites. These sites may be within the representation of another object,
and located by some computation implementing the semantics, such as finding
an appropriate offset into the current representation of a list, or a hash-accessed
bucket/slot of a dict etc.

BTW, the implementation of finding a binding site for a non-bare-name binding
is typically a method call with the method and arguments determined by the trailing
end of the assignment target expression and the object whose method is to be found
determined by the expression up to the trailing end.

Binding site implementation is not part of the language, but the semantics of
binding objects 1:1 or 1:many is.

Mutable objects have assignable binding sites. Immutable objects do not (except
for the "assignment" of initial binding references during their creation). Containing objects
contain binding sites, not values. Binding sites must have references to some
value-representing object or other if they semantically exist, though of course an implementation
may manage space any way convenient if semantics are preserved.

Going back to an expression again, when e.g., a name is evaluated, the name identifies
a binding site somwhere, and the value of any expression (including bare name) is, as we said at the start,
"a reference to an object representing the value of the expression."

In the case of a bare name, we have that reference as soon as we have identified the binding site
associated with the name, since that contains a reference to a value-representing object. If this reference
is now assigned to a binding site identified by another bare name, we have two bare names with respective
associated binding sites having references referring to the same value-representing object.

IOW, python shares value representations by default. Including mutable value representations. Which
means if you want a distinct mutable value equal to a given one, you need an expression that does
more than evaluate to find the reference to the given one -- i.e., creates a new value-representing object
using the given for information, and returns a reference the new.

To a C programmer, it will be tempting to say that "binding sites" are just pointer-data locations,
and references are pointers, and "value-representing-object" is just malloc'ed space for some custom
struct or array or both. This could be true for a particular implementation, but I think "binding site"
and "reference" as I have just used them, are also abstract concepts, capable of alternative, non-C
implementation of the python semantics. So I thought I'd try this as a possible terminology, to see if
it makes it any easier for anyone ;-)

Regards,
Bengt Richter
 
S

Steven D'Aprano

Steven said:
Mike Meyer wrote: [...]
Correct. What's stored in a list is a reference.

Nonsense. What is stored in the list is an object.

According to the Python Language Reference:

Some objects contain references to other objects; these are
called containers. Examples of containers are tuples, lists
and dictionaries.
[http://docs.python.org/ref/objects.html]


Is it so hard to understand that the word "reference" has a general,
imprecise meaning in common English (which is clearly how the quote
above is using the word) while still having in the context of assignment
and argument passing a more precise meaning which is dangerously
misleading?

Words are important -- not only for what they mean, but for what the
connotations they carry. For people who come to Python from C-like
languages, the word "reference" means something that is just not true in
the context of Python's behaviour. That's why people come to Python with a
frame that tells that what call by reference implies ("I can do this...")
and then they discover that they often *can't* do that.

It is a crying shame, because reference is a nice, generic word that just
cries out to be used in a nice, generic way, as the Python doc you quoted
is doing. But unfortunately, the term "reference" has been co-opted by C
programmers to effectively mean "pointer to a value".

The aim of the Python doc authors is to communicate information about
Python effectively. That means they have to be aware of words'
connotations and the mental frames they evoke. In the same way a good
programmer must work around bugs in the operating system or libraries, a
good author must work around bugs in people's mental frames which will
cause misunderstanding. That's why "reference" is a bad word to use in the
context of Python containers and argument handling: far from communicating
correct information effectively, it gives many readers a misleading
assumption about how Python code will behave.

The proof of this is the number of times people write to this newsgroup
confused about "call by reference" -- not because they came to Python with
assumptions about its behaviour, but because somebody told that Python was
"call by reference". Or that lists contain "references" to other objects.

In English, I can say "I'm writing a book on Russian history. I've
included a reference to Smith's classic work on the tsars of Russia."
That's a good, generic use of the word "reference". Nobody thinks that
anything I do to modify my book will effect Smith's work. Books don't work
that way.

But in programming, things do work that way. If my class Book contains a
reference to Smith's classic work, I can modify it. (Unless the language
deliberately restricts my ability to modify certain objects, as Python
does with immutable objects.)

That's what programmers expect when you talk about references, especially
if they come from a C (or Pascal) background. In Python, sometimes that's
true, and sometimes it is not, and the only way to tell is by looking at
the object itself, not by thinking about Python's high-level behaviour.

Thinking about Python's behaviour ("it always passes references to
objects") will invoke misleading frames in many programmers' minds. The
word "reference" is misleading and should be avoided, because what the
average non-Python programmer understands by the word is different from
what the experienced Pythonista understands by it.

If we were writing academic papers, we could define "call by reference"
and "objects contain references" any way we liked, and it would be the
responsibility of the readers to ensure they understood *our* meaning. But
we're not -- we're communicating information to ordinary programmers, many
of whom are kids still in school, not academics. Many of them will be
coming to Python with preconceived ideas of the meaning of "call by
reference", "assign a variable", etc. It is *our* responsibility to use
language that will not be misleading, that will not invoke incorrect
frames, that will not lead them up the garden path and be surprised by
Python's behaviour.

If we say "Python is call be reference" (or call by value, as many people
also say) we *know* the consequence will be newbies writing in saying "I
was told Python is call by reference, so I did this, and it didn't work,
is that a bug in Python? What is wrong?" It is not a bug in Python, it is
a bug in their mental model of how Python works, and we put that bug in
their head. Every time that happens, it is *our* fault, not theirs, for
using language guaranteed to mislead. If we use an unfamiliar term like
"call by object", the reader has no preconceived understanding of what it
means and won't be lead to incorrect assumptions.

We know that this will happen because it has happened time and time again
in the past. Are we incapable of learning from experience? Are we
intelligent sentient beings or do we just parrot what was said in the
past with no concern for the consequences of what we say?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,159
Latest member
SweetCalmCBDGummies
Top