2

I'm passing the value i to th = threading.Thread(target=func, args=(i,)) and start the thread immediately by th.start(). Because this is executed inside a loop with changing index i, I'm wondering if i inside the thread retaines its value from the time the thread has been created, or if the thread is working on the reference of i. In the latter case the value wouldn't necessarily be the sames as it was at creation of th. Are values passed by reference or value?

dlsf
  • 332
  • 2
  • 13
  • 1
    Neither, actually - Python variables are not symbolic names for memory locations (like in C or Pascal etc) but "name=>object" bindings so neither of the "by value" or "by reference" concepts apply. You definitly want to read this to understand Python "variables": https://nedbatchelder.com/text/names.html – bruno desthuilliers Oct 31 '18 at 11:12
  • Re, "...i inside the thread..." A thread does not have an "inside." The `target` function has an inside (i.e., its formal arguments and its local variables), but that's just like any other function. It's not because the function happens to be the `target` of a thread. – Solomon Slow Oct 31 '18 at 13:00
  • @SolomonSlow sure, that's what I'm referring to ;) – dlsf Oct 31 '18 at 13:56
  • I tested some cases by now and it seems using the target function of threading is following the definition of a regular function. So what really counts is, if either the object ("variable") is mutable or immutable. If it is mutable (e.g. a list), then in C language we're working with a pointer. If it is immutable the variable is called by value. – dlsf Oct 31 '18 at 14:14
  • See https://stackoverflow.com/questions/8056130/immutable-vs-mutable-types – dlsf Oct 31 '18 at 14:30

2 Answers2

2

I would say, passing mutable objects to functions is calling by reference.

You are not alone in wanting to say that, but thinking that way limits your ability to communicate with others about how Python programs actually work.

Consider the following Python fragment:

a = [1, 2, 3]
b = a
foobar(a)
if not b is a:
    print('Impossible!')

If Python was a "pass-by-reference" programming language, then the foobar(a) call could cause this program to print Impossible! by changing local variable a to refer to some different object.

But, Python does not do pass-by-reference. It only does pass-by-value, and that means there is no definition of foobar that could make the fragment execute the print call. The foobar function can mutate the object to which local variable a refers, but it has no ability to modify a itself.

See https://en.wikipedia.org/wiki/Evaluation_strategy

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57
0

Pass by value and pass by reference are two terms that can be misleading sometimes, and they don't always mean the same in every language. I'm going to assume we're taking about what the two terms mean in C (where a reference is passing a pointer to the variable).

Python is really neither of those, I'll give you an example I took from an article (all credit to the original writer, I'll link the article at the end)

def spam(eggs):
    eggs.append(1)
    eggs = [2, 3]

ham = [0]
spam(ham)
print(ham)

When spam is called, both ham and eggs point to the same value ([0]), to the same object. So, when eggs.append (1) is executed, [0] becomes [0, 1]. That sounds like pass by reference.

However, when eggs = [2, 3], now both eggs and ham should become the new list in pass by reference. But that does not happen; now eggs points to a list in memory containing [2, 3], but ham still points to the original list with the 1 appended to it. That bit sound more like pass by value.

EDIT

As explained above, if a parameter of the thread is modified inside it, the changes will be seen in the original thread as long as the parameter is mutable. That way, passing a list to the thread and appending something to it will be reflected in the caller thread, for example.

However, an immutable object can't be modified. If you do i += 1, you didn't modify the integer, integers are immutable in Python. You are assigning to i a new integer with a value one unit higher than the one before. It's the same thing that happened with eggs = [2, 3]. So, in that particular example, changes will not be reflected in the original thread.

Hope this helps!

Here's the article I promised, it has a much better explanation of the matter. http://stupidpythonideas.blogspot.com/2013/11/does-python-pass-by-value-or-by.html?m=1

Pablo Paglilla
  • 366
  • 2
  • 5
  • Re, "Python is really neither of those" and "...two terms that can be misleading..." It's only misleading for people who do not fully understand the difference between a heap-allocated object, and a variable that refers to the heap-allocated object. Once the student learns that difference, then it becomes plainly obvious that Python function call arguments are always passed by value. – Solomon Slow Oct 31 '18 at 12:54
  • 1
    "That said, given that i is the index of a loop in your example; it's going to be modified if you, for example, add 1 to it. The original thread will also have the new value" I tested it by now and this is not true. Because `i` is immutable, once it is passed to the target function of the thread, e.g. after `th = threading.Thread(target=func, args=(i,))` the value of `i` inside the target function is not connected to the "outside" anymore. – dlsf Oct 31 '18 at 14:07
  • @SolomonSlow Could you please clarify how this is connected to mutable/immutable objects? Because I would say, passing mutable objects to functions is calling by reference. Are heap-allocated objects immutable objects and variables referring to heap-allocated objects mutable? – dlsf Oct 31 '18 at 14:27
  • You're right @dlsf. But that's not because it becomes mutable when you spawn the thread. It's because Python integers are immutable by nature. Because of that, you can't modify the `i` that lives in your thread, just assign a new object to it. If you passed any mutable object, such as a list (or maybe a mutable wrapper for an int), it would change. I'll edit my answer as soon as a I can explaining that. – Pablo Paglilla Oct 31 '18 at 15:03