Passing a reference is semantically different from passing by reference (the latter being a functionality of the language); doing the former (behind the scenes) doesn't imply that the language supports the latter (if one wants to be rigorous, not even C supports pass by reference).
The canonical example is a function that swaps the variables passed to a function:
a, b = 10, 20
swap_fn(&a, &b)
a == 20 # true
Since the above are primitive data types (ints), in order to make this work, the language needs to generically support passing the address (reference) of the variables. Java¹/Python² etc. are not able to do this; they copy the value of the variable and send it to the function, which will operate on the copy.
¹=at least, last time I've checked, which was long ago :)
²=funny to think that at least in Python, one can mess with the global register of the variables, and actually accomplish that
I understand it is semantically different, but the interviewer should not be surprised you can change a variable in a function without returning it, which is the context of this thread. It's syntactic sugar for pointers.
Python 3.10.8 (main, Oct 13 2022, 09:48:40) [Clang 14.0.0 (clang-1400.0.29.102)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def f(d):
... d['foo'] = 'bar'
...
>>> a = {}
>>> f(a)
>>> a
{'foo': 'bar'}
That example actually holds with what the parent comment said. You aren't trying to modify d exactly, you're modifying d's contents.
Python 3.8.10 (default, Jun 22 2022, 20:18:18)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more
information.
>>> def f(d):
... d = {'foo': 'bar'}
...
>>> a = {}
>>> f(a)
>>> a
{}
does not modify the passed in dictionary, because the reference 'd' itself is passed by value. So the function doesn't change what d references for the caller. Java works the same way.
My understanding of what's going on with python is it passes by reference (hence what your code does), but if the data type is immutable any modification results in a new instance being created in the scope of the function, and the object passed in is not modified. Which mostly looks like pass-by-value behaviour.
I guess my understanding could be technically wrong in some way, but it seems to reflect what happens.
The issue is that Python passes references by value, so you cannot overwrite what the arguments of a function reference from the perspective of the caller. However, if you pass something like a dictionary or a class the function gets a copy of the reference and can use it to update the members of what was passed. Java operates on the same principle, although there may be some edge cases I'm not aware of where the two differ.
Perhaps I'm being simple minded, but the id of the passed object within the function is the same as the id of the object outside the function, no? My mental model is that an object's id in python is basically an address / pointer. So, how come it's the same inside the function as outside, if it's not pass by reference?
You're correct that the id is the same inside and outside the function, but when you modify the passed in argument the id changes because you're setting the value of the parameter, not what it's referencing. In C it'd be like changing the value of a pointer in a function instead of the value referenced by a pointer in a function.
Here's an example to include the id differences and the fact that a does not take on the value of d:
>>> def f(d):
... print(f"id: '{id(d)}'")
... d = {'foo': 'bar'}
... print(f"id: '{id(d)}'")
>>> a = {}
>>> a
{}
>>> id(a)
140362610196224
>>> f(a)
id: '140362610196224'
id: '140362610196160'
>>> a
{}
>>> id(a)
140362610196224
But, as I've posted elsewhere in this thread, this example will change the contents of 'd' since d itself is not being set, its contents are:
>>> def f(d):
... d['foo'] 'bar'}
...
>>> a = {}
>>> f(a)
>>> a
{'foo': 'bar'}
I don't believe they're saying the feature doesn't exist or never gets used, but rather it's rarely something that's taught to junior developers, and rarely something you see highlighted as a best practice.
I've been developing for 25+ years in any language you can imagine, and know how to use pass by ref just fine, and know there are situations where it might be the best solution.
But I can't remember the last time I've used pass by ref. It's really just a coding style quirk for me, I find it "ugly" and it breaks my train of thought when reasoning through the flow of code. I certainly don't begrudge anyone who uses it though.
And OP's anecdote about the interview is certainly disheartening. You'd hope the interviewer would at least be open to the idea of learning something new. I've learned countless things from developers I've interviewed over the years, and I was incredibly happy about it each time.
Edit: In re-reading your comment and below replies it seems you may be misunderstanding what's being discussed. Yes, the things we pass into and out of functions tend to be object references by default. But when we say "pass by ref" (in some languages at least) we mean, essentially, modifying a value in a calling function without actually returning anything from the called function. That's a horrible way to explain it, but the MS documents for the "ref" keyword do a good job of showing examples:
It's like the other day there was a post where someone wrote an entire blog post about how they changed a tight loop with a function call in Golang to pass a pointer to a struct instead of by-value so they stopped allocating and copying on each call.
Like...duh?
I suppose that's why I find Golang less weird to work with than others in my cohort. I spent a semester in the depths of C and OpenGL so I'm intimately familiar with by-value vs by-ref.
Yes, I understand... But the result, practically speaking, is objects are passed by reference, just like I said. Methods/functions can modify their parameters.
It's been a while, but my understanding was that actual C++ references were immutable, and what you are describing can only happen as a result of passing a pointer by value.
Yeah, you cannot 'reassign' a reference. The compiler will yell at you if you try. You can definitely modify values of your referenced object though, that's fair game. When it comes to pointers you can do whatever the hell you want but be careful.
All those languages pass by value and it's kind of embarrassing that you mixed it up while criticizing someone else for not knowing it.
One necessary (but insufficient) test you can use to determine if your language has call by reference or call by value is whether you can implement a swap function. In the languages you list, a swap function is not possible to implement, whereas in C++ it is, which tells you that those languages do not implement pass by reference.
We are actually both wrong. If they passed objects by value you'd be modifying a copy. They technically "pass by object reference" as another poster mentioned.
ECMAScript's Language Specification also states in section 10.6 that it uses pass by value semantics, although it's much more formal about the specific approach it uses:
I can't link to the specific section but you can review the semantics of MakeArgGetter and MakeArgSetter which are specified to produce arguments bound to the *value* associated with the name, as opposed to a reference.
Python does not have a spec that I can reference, but given that its argument semantics follows those of Java, and once again the inability to write a swap function, it should not be too difficult to deduce that Python also passes by value.
This is indeed correct. Object references themselves in Java/Python are passed by value, which is why the following two code samples do not have the same effect:
Python 3.8.10 (default, Jun 22 2022, 20:18:18)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def f(d):
... d['foo'] 'bar'}
...
>>> a = {}
>>> f(a)
>>> a
{'foo': 'bar'}
Python 3.8.10 (default, Jun 22 2022, 20:18:18)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def f(d):
... d = {'foo': 'bar'}
...
>>> a = {}
>>> f(a)
>>> a
{}
I said they pass objects by reference, which is true. Technically, yes, Java is pass by value. However, objects are not passed by value. The value you are passing is a reference (pointer) to that object. If this were not the case, you would not be able to see modifications to method parameters reflected in the caller because you're modifying a copy. You are not modifying a copy.
I think you are deeply confused about this topic and should not be making judgements about other people. Instead of making an honest attempt to understand the difference between pass by value and pass by reference, you are trying to mince words to save face and even your attempt to mince words is incorrect.
While I likely can not convince you further that you are in error, especially since you're now trying to double down on this, for others reading this who have a genuine desire to understand this topic, please understand that Java, JavaScript and Python do not pass by reference, but instead pass by value and refrain from attempting to redefine technical terminology.
The references I cite are quite authoritative and unambiguous on this topic.
I'm definitely nitpicking here, but I'd say almost all languages use pass-by-value, and what you're talking about is copying a pointer. C++ actually has true pass by reference, nothing is copied in memory.
In addition to pass by value and by reference, there's also a pass by descriptor mechanism. It's not widely supported, but it's just another way to glue different conventions for defining arrays in various languages, where it's not just a raw pointer to alloc'd memory.
The canonical example is a function that swaps the variables passed to a function:
Since the above are primitive data types (ints), in order to make this work, the language needs to generically support passing the address (reference) of the variables. Java¹/Python² etc. are not able to do this; they copy the value of the variable and send it to the function, which will operate on the copy.¹=at least, last time I've checked, which was long ago :)
²=funny to think that at least in Python, one can mess with the global register of the variables, and actually accomplish that