2

Heyo, I've run upon a problem. So I have three files:

  1. Main Program
  2. Functions
  3. Data

And I want the Main Program to call a function from the Functions module, which changes a variable in Data. Then I need to use the new variable elsewhere in the Main Program.

This is what I want, shown as a simplified demonstration program:

The Data file:

#data.py
#just an short depiction of my actual file
text = ""

The Functions file:

#functions.py
from data import *

def printHi():
    global text
    text = "hi"
    print(text)

The Main Program:

#mainProgram.py
from functions import *
from data import *

printHi()
print(text)

What I expected would happen would be that when I run the Main Program:

The Functions file and Data file is imported.

Then it calls the "printHi" method from the Functions file.

The variable "text" from the Data file is assigned "hi", and is printed.

The the Main Program prints the "text" variable.

And I supposed that the text variable would be "hi". However, to my disappointment, it prints blank. It does indeed print the initial text value.

I really have NO idea why this is so. Shouldn't the text variable have already been changed? Could you please explain what part about my program is wrong and how to correct it?

0

2 Answers 2

3

The short answer is simply not to do this. It's a Bad Idea for all the reasons that global variables are always bad ideas: because they lead to stack overflow questions that read like "If I do this thing I shouldn't do, it does something I didn't expect -- why did it do that?" Whose easiest answer is, as you've now read, "That's why you shouldn't do that thing."

The long answer is a bit beyond me without spending a whole lot more time on the matter, but the longer answer is simple enough. When you do those "star" imports (from modulename import *) you're rebinding the name of the variable. What functions.printHi thinks of as text is not data.text but actually functions.text. When it's changed in printHi, it changes functions.text which should still be okay, since mainProgram is also importing functions.

However remember that mainProgram ISN'T actually importing functions, it's from functions import *'ing. That means what mainProgram thinks of as text is neither data.text nor functions.text, but mainProgram.text. When functions.printHi changes functions.text, it doesn't touch mainProgram.text.

The short answer applies here because these sorts of pitfalls are non-obvious unless you can think deeply enough about your code to understand them. If you are able to think that deeply about your code, you should be able to write something that can sidestep such pitfalls entirely. For instance: "global mutable state" is generally a bad thing. Avoid it.

To just make this work, drop all your "star" imports. The following code works:

# functions.py
import data


def printHi():
    # plus! You don't need the `global` anymore.
    data.text = "hi"
    print(data.text)

 

# mainProgram.py
import functions
import data


functions.printHi()  # prints "hi" from inside functions.printHi
print(data.text)     # also prints "hi"
Sign up to request clarification or add additional context in comments.

4 Comments

Couldn't he make an instance var with an object? Then just save it with pickle.
@thesonyman101 that seems much more complicated than necessary for a beginner who needs to learn general programming basics first.
I haven't tested this, but I do wonder if this behavior would be the same if the object in question wasn't a string but a mutable object like a list. These are the sorts of questions you simple avoid altogether when you take a more reasonable approach to data sharing. PARAMETERIZE!
@Adam Smith great question. I didn't think there was any way the import statement would copy the list, so when you modified it by means of a function in another module it would have to change the original list. To prove that, I created a list in one file, imported it with one of those stupid * imports, and modified it in a second file. It turned out as I expected: there's only one list. Hooray - another way to write a screwed up program. BTW loved the first paragraph of your answer.
2

Cool, we have a lot of people saying "don't do this!". Well, what should you do then? The good way to do this is to pass the text variable into the function and out of it. Like so:

The Data file:

#data.py
#just an short depiction of my actual file
text = ""

The Functions file:

#functions.py
from data import *

def printHi(atext):
    atext = "hi"
    print(atext)
    return atext

The Main Program:

#mainProgram.py
from functions import *
from data import *

text = printHi(text)
print(text)

That solves your problem. You should probably also get rid of the * imports as the other answer suggests but that's a philosophical question.

1 Comment

I'm afraid this probably won't solve the deeper problem, only the minimal example posted here. My educated guess is that his program is a poorly-implemented Finite State Machine and simple returning a modified value, while useful, won't modify global state, so it will break his program in other subtle ways. This is, however, an excellent answer to the question as-asked.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.