1

I have multiple python files in different folders that work together to make my program function. They consist of a main.pyfile that creates new threads for each file and then starts them with the necessary parameters. This works great while the parameters are static, but if a variable changes in the main.py it doesn't get changed in the other files. I also can't import the main.py file into otherfile.py to get the new variable since it is in a previous dir.

I have created an example below. What should happen is that the main.py file creates a new thread and calls otherfile.py with set params. After 5 seconds, the variable in main.py changes and so should the var in otherfile (so it starts printing the number 5 instead of 10), but I haven't found a solution to update them in otherfile.py

The folder structure is as follows:

|-main.py |-other | otherfile.py

Here is the code in both files:

main.py

from time import sleep
from threading import Thread

var = 10

def newthread():
    from other.otherfile import loop
    nt = Thread(target=loop(var))
    nt.daemon = True
    nt.start()

newthread()

sleep(5)

var = 5 #change the var, otherfile.py should start printing it now (doesnt)

otherfile.py

from time import sleep

def loop(var):
    while True:
        sleep(1)
        print(var)
Mihkel
  • 689
  • 7
  • 34

2 Answers2

1

In Python, there are two types of objects:

  1. Immutable objects can’t be changed.
  2. Mutable objects can be changed.

Int is immutable. you must be use list or dict variable.

from time import sleep
from threading import Thread

var = [10]

def newthread():
    from other.otherfile import loop
    nt = Thread(target=loop, args=(var,), daemon=True)
    nt.start()

newthread()

sleep(5)

var[0] = 5
Vahid Bahramian
  • 134
  • 1
  • 7
0

This happens because of how objects are passed into functions in Python. You'll hear that everything is passed by reference in Python, but since integers are immutable, when you edit the value of val, you're actually creating a new object and your thread still holds a reference to the integer with a value of 10.

To get around this, I wrote a simple wrapper class for an integer:

class IntegerHolder():
    def __init__(self, n):
        self.value = n

    def set_value(self, n):
        self.value = n

    def get_value(self):
        return self.value

Then, instead of var = 10, I did i = IntegerHolder(10), and after the sleep(5) call, I simply did i.set_value(5), which updates the wrapper object. The thread still has the same reference to the IntegerHolder object i, and when i.get_value() is called in the thread, it will return 5, as required.

You can also do this with a Python list, since lists are objects — it's just that this implementation makes it clearer what's going on. You'd just do var = [10] and do var[0] = 5, which would work since your thread should still keep a reference to the same list object as the main thread.

Two more errors:

  1. Instead of Thread(target=loop(var)), you need to do Thread(target=loop, args=(i,)). This is because target is supposed to be a callable object, which is basically a function. Doing loop(var) will cause the Thread constructor to loop forever waiting for the function to return (and then set target to the return value), so the thread never actually gets created. You can verify this with your favorite Python debugger, or print statements.

  2. Setting nt.daemon = True allows main.py to exit before the thread finishes. This means that as soon as i.set_value(5) is called, the main program terminates and your integer wrapper object ceases to exist. This makes your thread very confused when it tries to access the wrapper object, and by very confused, I mean it throws an exception and dies because threads do that. You can verify this by catching the exit code of the thread. Deleting that line fixes things (nt.daemon = False by default), but it's probably safer to do a nt.join() call in the main thread, which waits for a thread to finish execution.

And one warning, because programming wouldn't be complete without warnings:

  1. Whenever different threads try to access a value, if AT LEAST ONE thread is modifying the value, this can cause a race condition. This means that all accesses at that point should be wrapped in a lock/mutex to prevent this. The Python (3.7.4) docs have more info about this.

Let me know if you have any more questions!

chang_trenton
  • 827
  • 6
  • 10