The idea I had about imports until now is that, at least if you used the from x import *
the file you are importing would just be completely copied into your code.
In order to help myself organize my code better, I started deviding it into different files, but, when it came to global variables at least, they dont seem to work anymore.
I have something like this:
import threading
run = False
def do_something():
global run
while True:
if run == False:
break
print("Anything")
return 0
def start_do_something():
global run
x = threading.Thread(target=do_something, args=(,))
x.start()
run = True
return 0
def start():
global run
while True:
print("1 - Do something\n2 - Stop something")
global run
n = int(input())
if n == 1:
start_do_something()
elif n == 2:
run = False
else:
return 0
start()
The code is obviously more complex but the point is that I separated it into two files like in the following example
Do_something.py:
run = False
def do_something():
global run
while True:
if run == False:
break
print("Anything")
return 0
def start_do_something():
global run
x = threading.Thread(target=do_something, args=(,))
x.start()
run = True
return 0
Start.py:
import threading
from Do_something import *
def start():
global run
while True:
print("1 - Do something\n2 - Stop something")
global run
n = int(input())
if n == 1:
start_do_something()
elif n == 2:
run = False
else:
return 0
start()
The code works as intended in the first example, when i input the value 2, the do_something
thread/function stops outputting, but in the second, it seems like the start
function is not being able to change the run
variable and thus, remains unable to finish the do_something
function as it keeps printing.
Is there a way to just import everything as it is on the file? It isn't really necessary but it would help a lot when it comes to organization and readability of my code, at least for me. Also, I understand its bad practice, I'm just trying a couple of stuff, and kind of teaching myself and I believe this way I could organize myself better
CodePudding user response:
You're combining two things that are generally bad ideas:
from module import *
, you should generally avoid this.- using
global
from a function to bind global variables, particularly a variable that was imported.
Your reasoning seems to be:
- define a global variable
run
infile1.py
- import that global variable from
file2.py
- modify that global variable from a function
start()
infile2.py
, usingglobal
- then access the same global variable in another function in
file1.py
, also usingglobal
That seems like it might work on the surface, but that's not how names work in Python. (they might have, but they don't - this is not how you're supposed to be passing values around)
What really happens:
- define a global variable
run
infile1.py
this part is OK, although using a global variable already has a bad code smell. - import that global variable from
file2.py
this is also OK, you'd find that if you only accessed the value anywhere infile2
(assuming it's not shadowed anywhere), that it works as expected. However, since you usedimport *
, nobody will be able to say where exactly that variable was defined. - with
global run
andrun = False
, you rebindrun
within thefile2
namespace. It no longer refers to the same value andrun
infile2
andrun
infile1
now refer to different objects. - with
global run
instart_do_something()
, you tell Python that you want to be able to rebind the global variablerun
, but you have no intention of doing so as the function doesn't (re)definerun
.
Note that removing the global run
from start_do_something()
doesn't help, because run = False
for the global run
in start()
still rebinds the name and you're dealing with 2 separate variables after that.
You could define an object that defined whether you should run or not in file1
and call a method on it to switch it. That would work, but still be very bad code:
file1.py
:
class Run():
value = True
run = Run()
def do_something():
# no "global" needed to refer to a global variable
if not run.value:
return
print("Anything")
return 0
file2.py
:
from file1 import *
def start():
# no "global" needed to refer to a global variable
n = int(input())
if n == 0:
run.value = False # avoid rebinding global run
return 0
start()
do_something()
However, you should just steer clear of globals, for many obvious reasons and non-obvious ones like the one I explained above.
A language like Python has global
because there are rare cases where it is needed, and it would be a worse language without it. It does not have global
as the preferred way of passing data around. Many new programmers make the mistake of viewing global
as the be all end all of making things available in their code, but it's a dead end. Perhaps this is the result of coming from other languages where using globals makes more sense, but in Python you should try to define things in appropriate modules. Then just use what you defined, without trying to redefine it. Generally pass explicitly to a function what it needs, instead of the function relying on it being available in the current namespace.
CodePudding user response:
1) Put all global properties into a class
...and reference them with dot notation. The class variables are singular and instead of reassigning a variable, you mutate the state of an object, so you may find it useful, though that is not a very pythonic thing to do.
globs.py
class GlobalVars:
foo = 0
bar = False
fizz = "hello"
main.py
from globs import GlobalVars
print(GlobalVars.foo)
GlobalVars.foo = "New value"
print(GlobalVars.foo)
2) Keep global variables hidden for other files and use getters and setters
That keeps the global variables protected from other modules, but you will have to call getters and setters a lot, so I find the first way better in terms of readability.
globs.py
some_global_var_x = 100
def set_global_var_x(new_value):
global some_global_var_x
some_global_var_x = new_value
def get_global_var_x():
return some_global_var_x
main.py
from globs import set_global_var_x, get_global_var_x
print(get_global_var_x())
set_global_var_x("Hello")
print(get_global_var_x())
3) However, You can still also combine several small files into one. (Not recommended)
That is not how things are done in Python, but if that's what you want...
Consider the following project structure:
-src
| -snippet_1.py
| -snippet_2.py
| -snippet_3.py
| -snippet_4.py
| -...
-gen.py
The folder src
contains your project broken into small and manageable pieces.
gen.py
is the file that generates the final file with the code you will run.
gen.py
import os
with open('combined_code.py', 'w') as destination:
for filename in os.listdir('src'):
code_file = os.path.join('src', filename)
# we do not consider directories
if not os.path.isfile(code_file):
continue
with open(code_file) as source:
# Extra newline just in case you forget to put newline at the end of the file
destination.write(source.read() "\n")
snippet_1.py
x = 1
snippet_2.py
print(x)
snippet_3.py
x = 1
snippet_4.py
print(x)
Running
> python gen.py
> ls
src combined_code.py gen.py
> python combined_code.py
1
2
Generated combined_code.py
x = 1
print(x)
x = 1
print(x)
As you can see, there are no imports, but there are definitely problems. In this approach, you have to ensure the order of the code if that matters. Also may be hard to debug and you have to re-"compile" everything every time you use it. Just for your information and curiosity.