I have a module that does something one of two ways:
project/
|-- main.py
+-- module.py
main.py
import module
module.do_something()
module.set_method(module.WAY_2)
module.do_something()
module.py
WAY_1 = "the_first_way"
WAY_2 = "the_second_way"
method = WAY_1 # by default
def set_method(new_method):
method = new_method
def do_something_the_first_way():
print "Doing it the first way"
def do_something_the_second_way():
print "Doing it the second way"
def do_something():
if method == WAY_1:
do_something_the_first_way()
if method == WAY_2:
do_something_the_second_way()
When I run main.py, I get this output:
Doing it the first way
Doing it the first way
It looks like the method variable of module.py is not getting updated, even though we try to set it with set_method from main.py. I have an idea of what's happening here based on this question, but I want to know what the best way to fix the problem is.
What is the most elegant, Pythonic way to solve this problem?
By default, any assignment in a function creates a function-local variable; it does not update a global by the same name. You need to define it as global explicitly:
def set_method(new_method):
global method
method = new_method
Related
how do you mock a global variable in pytest? Here is a pair of example files:
File being tested, call it main.py:
MY_GLOBAL = 1
def foo():
return MY_GLOBAL*2
def main()
# some relevant invokation of foo somewhere here
if __name__=='__main__':
main()
File that is testing, call it test_main.py:
from main import foo
class TestFoo(object):
def test_that_it_multiplies_by_global(self):
# expected=2, we could write, but anyway ...
actual = foo()
assert actual == expected
This is just a dummy example of course, but how would you go about mocking MY_GLOBAL and giving it another value?
Thanks in advance i've been kind of breaking my head over this and i bet it's really obvious.
The global variable is an attribute of the module, which you can patch using patch.object:
import main
from unittest.mock import patch
class TestFoo(object):
def test_that_it_multiplies_by_global(self):
with patch.object(main, 'MY_GLOBAL', 3):
assert main.foo(4) == 12 # not 4
However, you want to make sure you are testing the right thing. Is foo supposed to multiply its argument by 1 (and the fact that it uses a global variable with a value of 1 an implementation detail), or is it supposed to multiply its argument by whatever value MY_GLOBAL has at the time of the call? The answer to that question will affect how you write your test.
It's useful to distinguish between module-level constants and global variables. The former are pretty common, but the latter are an anti-pattern in Python. You seem to have a module-level constant (read-only access to the var in normal production code). Global variables (R/W access in production code) should generally be refactored if possible.
For module constants:
If you can do so, it's generally more maintainable to refactor the functions that depend on module constants. This allows direct testing with alternate values as well as a single source of truth for the "constants" and backward compatibility. A minimal refactor is as simple as adding an optional parameter in each function that depends on the "constant" and doing a simple search-and-replace in that function, e.g.:
def foo(value=MY_GLOBAL):
return value*2
All other code can continue to call foo() as normal, but if you want to write tests with alternate values of MY_GLOBAL, you can simply call foo(value=7484).
If what you want is an actual global, (with the global keyword and read/write access during production code, try these alternatives.
Sometimes, I see examples like this, but I don't understand how do they work. Imported module uses function without any places in which this function is set to use. Please can someone explain me how to use them.
Example:
from some_package import *
def some_func():
# do_something
pass
imported_func()
And then imported_func somehow defines some_func and uses it. How is this implemented?
When I tried to call some_func from module.py I received an error. Again: idea is to use function from imported file which was defined in importing file. I couldn't find answer in google.
I tried:
from f.module import *
obj = cls()
def some_func():
for _ in range(100):
print("smth")
obj.imported_func()
Code in main.py
class cls:
#staticmethod
def imported_func():
some_func()
Code in module.py
I have main.py and folder f in one directory. In folder f I have module.py
The way to do this is at first import __main__ then call __main__.some_func(), but remember, it's not a good practice because at least you are reserving name, what can become common reason for errors.
I am trying to find a way to share a variable between multiple python scripts. I have the following code:
b.py
my_variable = []
a.py
from b import my_variable # import the value
def run():
global x
x = my_variable.append("and another string")
print(my_variable)
if __name__ == '__main__':
run()
c.py
import a
print(a.x)
a.py runs just fine without giving any error. However, when I run the c.py file, it gives off the following error:
Traceback (most recent call last):
File "E:/MOmarFarooq/Programming/Projects/Python Projects/Variables across files/c.py", line 2, in
<module>
print(a.x)
AttributeError: module 'a' has no attribute 'x'
What I want the code to do is, print the new value of my_variable after it has been changed in a.py . Is there any way I can do that?
the error occurred because you never called the run function from a.py. The if __name__=='__main__': statement is only satisfied if you are running a.py as a program, not importing it as a module.
So a.py should be
from b import my_variable # import the value
def run():
global x
x = my_variable.append("and another string")
print(my_variable)
run()
Note that x will be set to None because the append function does not return anything. It just appends a value to a list.
You need to call the run() function in your c.py file.
Here's how the code should be:
import a
a.run()
Well, module 'a' does have no attribute 'x'. Module 'a' has a function that creates a variable 'x', but as long as the method isn't called, the attribute isn't there.
You could change file c.py to:
import a
a.run()
print(a.x)
Another solution would be to make sure that the run() function is always called when importing module 'a'. This is currently not the case, because of the line if __name__ == '__main__':.
If you don't want to run the code but only want to make sure the variable exists, just define it in the module. Before the definition of your run() method, just add x = None (or use any other initial value you prefer).
Note, however, that there other problems with your code and that using globals in this way is a really bad programming pattern, which will likely lead to other problems later on. I wonder what you want to achieve. It probably would be a better solution if you could pass x as argument to the run() function instead of referring to a global variable. But that's outside the scope of this question and difficult to answer without more information.
I have 2 files:
Main.py
from test import test
def main():
sol = 'hello'
if test() == sol:
print('Yes')
else:
print('No')
test.py
def test():
return 'hello'
Is there a way to access the sol variable in my test function which is in another file? I'd like to do something like this:
def test():
return sol
This way it's always the same as the variable in Main.py. I tried a solution which is mentioned on Python extract variables from an imported file but it didn't work for me. Thank you for any help
Edit: I'd like to do it without changing the Main.py file
Since sol isn't defined in the function you will need to declare it as a global function so it can be used in the function. Change test.pyto the following...
test():
global sol
return sol
Hope that helps.
If sol is a constant (i.e. a variable which will not change during the course of the program), then you can put it into a separate file, say var.py, and import it into both main.py and test.py:
from var import sol
BUT what you will be importing is a copy of the variable with the value it had at the time it was imported - any subsequent reassignments will not update the value of sol in test.py and main.py. Because a string is immutable, when you reassign a value to it what you are actually doing is you are reusing the variable name for a new entity.
What you need to do is have your variable in a mutable structure, such as a list or a class, so your var.py will look like this:
class v(object):
sol = 'Hello'
and then in main.py and test.py you can refer to sol using:
from var import v
print(v.sol)
This way, any changes to v.sol will be correctly reflected anywhere class v is imported. A bit cumbersome, but that's how it is in Python.
You can use inspect module to get the sol variable from main function without change anything of main.py. Of course, you need to call main function.
Main.py:
from test import test
def main():
sol = 'hello'
if test() == sol:
print('Yes')
else:
print('No')
main()
test.py:
import inspect
def test():
frame = inspect.currentframe()
sol_from_main = 'default'
try:
sol_from_main = frame.f_back.f_locals['sol'] # line A
except:
print('No sol found')
print(sol_from_main) # this print 'hello'
return sol_from_main
Output:
hello
Yes
Explanation:
From the python doc, we can see next:
frame
f_back | next outer frame object (this frame’s caller)
f_locals | local namespace seen by this frame
So line A get the caller of current function, that is main function, and use f_locals to get all local variables of main function, so it can get the sol value from main. FYI in case you still need it.
this is probably a dumb question, but wasnt too sure what else to do.
main.py
import module2
global x
hello="Hello"
x=module2.message()
x.say()
module2.py
class message:
def say():
print hello
When I print hello, i am referring to the hello variable in main.py however this method will return an error. Does anyone know the solution? (i would prefer not to pipe the hello variable into the function)
The only reliable solution is called encapsulation.
So, basically, you could change your code to look like that:
main.py
import module2
global x
hello="Hello"
x=module2.message(hello)
x.say()
module2.py
class message:
def __init__(self, hello):
self.hello = hello
def say():
print self.hello
Plus try to follow coding style of Python - life of you and future developers of your code will be easier.
Multiple options, but do note that one module cannot ever access the calling module directly.
Simply pass hello as a variable (def say(msg): print msg)
Pass all variables in main.py to module2: def say(g): print g['hello'] and say(globals())
Store it somewhere, then extract it when you need it.
Since main.py imports module2.py, you can access the globals defined in moule2 in main.
In your case since module2 is not importing main, so the globals in main is not accessed in module2.
one solution is that defined by #Tadeck
In this particular example, it's totally OK for module2.py to import main.py, there are some gotcha's though.
The most obvious is that main.py is probably being run from the command line, like python main.py, which has the effect of making that file think it's called __main__. You could import that module in module2, but that's sort of unusual; it's called "main.py" and you want to import main. Doing so will cause the module to be imported a second time.
For that to be OK, you have to arrange for importing the file to have no side effects unless it's imported as __main__. A very common idiom in python is to test that condition at the end of a module.
import module2
global x
hello="Hello"
def main():
x=module2.message()
x.say()
if __name__ == '__main__':
main()
And now it's just fine for module2.py to actually import main. On the other hand, importing variables from one module into another gets hard to predict when the imports can be recursive, you may not have that variable yet because the module is already trying to import you. On the other hand, it's always safe to refer to a variable in a module using dotted syntax. So your module2.py should be:
import main
class message:
def say():
print main.hello
which also makes it more obvious just where hello came from.