Importing variable names into class namespace - python

Is it possible to import global variables into an object's instance namespace?
In a structure like this:
./mypackage/module1.py
./config.py
./script1.py
If config.py has:
#config
MYSETTING=1
and script1.py has:
#SCRIPT1
from config import *
from mypackage.mymodule import MyClass as MC
obj=MC()
module1.py:
#Myclass
class MyClass(object):
def test(self):
print MYSETTING
will give error NameError: global name "MYSETTING" is not defined
Is there some way I can import variables from config.py into MyClass namespace as globals without doing enumerated "global x" on each of them?

This problem is a little confusing. There are three parts.
config: contains variables you want.
script: contains your logical code and variables imported from config.
module: contains functions' definition.
When you call MC.test, it will find MYSETTING in module's namespace, but what you want is to find in script's namespace. So in test function, what you real want is to access parent frame's global namespace.
In this case, you need to use inspect built-in module.
import inspect
print(inspect.current_frame().f_back.f_globals["MYSETTING"])
inspect.current_frame() will return current frame, and f_back refers to parent frame which is the frame calling this function, then f_globals refers to frame's globals()

Related

Confusing python variable scope

I usually don't think too hard about variable scope in python, but I wanted to see if there's a clean explanation for this. Given two files called main.py and utils.py:
utils.py
def run():
print(L)
main.py
import utils
def run():
print(L)
if __name__ == '__main__':
L = [1,2]
run()
utils.run()
The first run() call in main.py runs fine despite L not being fed into run(), and the utils.run() call raises a NameError. Is L a global variable available to all functions defined in main.py?
If I imported utils with from utils import * instead of import utils, would that change anything?
It's module-level scope. A global variable defined in a module is available to all functions defined in the same module (if it's not overriden). Functions in another module don't have access to another module's variables unless they import them.
About "If I imported utils with from utils import * instead of import utils, would that change anything?":
No. The scope is determined at parsing time.
Check
this
for more information.
Notably:
It is important to realize that scopes are determined textually: the global
scope of a function defined in a module is that module’s namespace, no matter
from where or by what alias the function is called. On the other hand, the
actual search for names is done dynamically, at run time [...]
So the global scopes of both functions for variables defined in a module are the modules they're defined in. For one, its module also later has a definition for a global variable it uses, but not the other module, and when it's time to check for a variable when a function is run, each checks their own module's variables definitions, one finds it, the other does not.
See Python's FAQ. Their implementation of scope is a compromise between convenience and the dangers of globals.
Variables are treated as globals if they're only referenced by a function, and need to be explicitly declared as globals (e.g. global foo ) inside of the function body if you want to edit them. If you edit run() to try and change the value of L, you'll get an error.
What's happening here is that your Python code imports utils, and then runs run(). This function sees that you're looking for a variable named "L," and checks your global namespace.

Updating an Imported Object

Suppose file_A.py is written as so:
#file_A.py
my_object = create_new_object()
def update_object():
global my_object
my_object = update(my_object)
and then in file_B.py we do as so:
#file_B.py
from file_A import my_object, update_object
def process_object(object):
#do some operation ...
process_object(my_object) #first call to process_object()
update_object()
process_object(my_object) #second call to process_object()
My question is, when the second call to process_object() is made, will it use the original version of my_object ,which is imported at the top of file_B.py, be used, or will it use the updated version which replaces my_object when the update_object() function is called from file_B.py?
Variables are passed by reference. In file_A you will update the modules reference to my_object but the value imported in file_B will still have the old reference. For example:
Say I have file testA.py
a = 1
def foo():
global a
a = 2
And testB.py
from testA import a, foo
print(a)
foo()
print(a)
If I run testB.py you will get the output 1,1
This is because the global only updated the reference attached to the module testA.py. If you were to access the module however, then you would see it did update there. An example where you access the module instead
testC.py
import testA
print(testA.a)
testA.foo()
print(testA.a)
This will output 1, 2
Note that the same variable (even if global) can have different values in different python modules. Thus the best way to reference a variable from another module is by using module.variable
So your code might look like
import file_A
print(file_A.my_object)
file_A.process_object(file_A.my_object)
file_A.process_object(file_A.my_object)
print(file_A.my_object)
The key to referencing the right variable is to use the module name.
This can also be useful when you are working with multiple files and you need to keep some global variables.

How to fix 'ImportError: cannot import name' upon sharing global variables between files

I have params on class SignIn, and want to use it on another class, but compiler says 'ImportError: cannot import name 'HEADERS_CONTENT_TYPE' from 'api.sign_in_page'
What should I do for importing global variables?
I've tried to declare variable as global before class declaration and just import them on the second class
global HEADERS_CONTENT_TYPE
global TOKEN_PARAM
class SignInPage:
from api.sign_in_page import HEADERS_CONTENT_TYPE, TOKEN_PARAM
class SignalsAddPage:
result = requests.post(url=ProjectConfigReader.SIGNALS_ENDPOINT, json=json_for_create_signal, headers={HEADERS_CONTENT_TYPE, TOKEN_PARAM})
I expect the new signal has been created, but
ImportError: cannot import name 'HEADERS_CONTENT_TYPE' from 'api.sign_in_page' error occurs
The global keyword is used inside a function to set a value for a variable in the global scope. Import between modules does not require the use of the global keyword. See the FAQ below to help you.
https://docs.python.org/3/faq/programming.html#how-do-i-share-global-variables-across-modules
You don't need to use global. Just set values to variables and then you can import them.
global needs to defer that name of variable inside functions scope is refer to same value that variable with same name outside of it.

Why does Python print this global variable instead of class attribute?

I'm trying to understand when Python code will refer to module-level variables versus class level variables. I have the following code in a module, main.py'
## main.py
# global variable x
x = "I am global and I love it!"
class SomeClass:
x = "I am a class attribute, and life is good!"
print(x) # prints "I am a class attribute, and life is good!"
def __init__(self):
print(x) # prints "I am global and I love it!" Why?
print(x) # prints "I am global and I love it!"
SomeClass()
When I import this module, the output is:
I am a class attribute, and life is good!
I am global and I love it!
I am global and I love it!
Why does the print inside the SomeClass.__init__ method print the global variable, while the print inside the class body prints the class attribute x?
This was paraphrased from a question on the Python mailing list: https://mail.python.org/pipermail/python-list/2015-December/701167.html
Class definitions in Python create a namespace, but they do not create a new scope. This means that functions defined inside the class cannot access variables of the containing class directly. Instead they must access them as attributes of the class or an instance.
So, rather than accessing x in your example's __init__ function, you can get the class variable by using SomeClass.x or self.x.
The reason that print(x) works in the class body is that that code is running with the class namespace as its local namespace. There are a number of issues you can run into if you try to do more complicated stuff at class level though, as the scopeless namespace is a rather weird environment. For example, this will not work:
class Foo:
x = 1
dct = {i: x for i in range(10)}
You'll get a NameError about the x variable because the dictionary comprehension runs in its own scope and cannot see the class variable x, which is defined in the scopeless class namespace.
Importing a module
Whenever you import a module, Python executes all the module-level code in the order the module is written. It attaches all the defined names to the module object, and the importer of the module can access all the defined names through the module object. That way you can import the module, and the names in the module don't clobber your namespace.
import main # executes the script main.py, then creates a module
print(main.x)
Defining a class
You can think of the way that Python creates the class as similar to how it creates a module: it executes all the code in the class body, then assigns the defined names to the class. The __init__ method just becomes an attribute of the class, as well; by the time the function is called, the class has been constructed and within the function you can only refer to the x class attribute as SomeClass.x (or self.x inside of __init__). The namespace for the class definition is different than the namespace for the module, so the print(x) inside the class finds the class namespace first, since the print is in the same namespace.
Aside
Just as you can't refer to the current module when you're in, you cannot refer to the current class either. For example:
class SomeOtherClass:
x = 5
SomeOtherClass.x = 6 # This fails!
You can't refer to the class for the same reason you cannot refer to the module you are in from within a module: the name has not been created yet.
This answer was inspired by ChrisA's answer on the Python mailing list.

conditionally import module to shadow local implementation

I am writing a Python script where some of the core functionalities can be done by another existing library. Unfortunately, while that library has more features, it is also slower, so I'd like if the user could at runtime select whether they want to use that library or my own fast and simple implementation. Unfortunately I'm stuck at a point where I don't understand some of the workings of Python's module system.
Suppose that my main program was main.py, that the (optional) external module is in module_a.py and that my own fast and simple implementation of module_a together with the actual program code that uses either my own implementation or the one of module_a is in the file module_x.py:
main.py:
import module_x
module_x.test(True)
module_x.test(False)
module_a.py:
class myclass():
def __init__(self):
print("i'm myclass in module_a")
module_x.py:
class myclass():
def __init__(self):
print("i'm myclass in module_x")
def test(enable_a):
if enable_a:
try:
from module_a import myclass
except ImportError:
global myclass
enable_a = False
else:
global myclass
i = myclass()
When I now execute main.py I get:
$ python3 main.py
i'm myclass in module_a
i'm myclass in module_a
But why is this? If False is passed to test() then the import of the module_a implementation should never happen. Instead it should only see myclass from the local file. Why doesn't it? How do I make test() use the local definition of myclass conditionally?
My solution is supposed to run in Python3 but I see the same effect when I use Python2.7.
An import statement is permanent within the thread of execution unless it is explicitly undone. Furthermore, once the from ... import statement is executed in this case, it replaces the variable myclass in the global scope (at which point the class it was previously referencing defined in the same file is no longer referenced and can in theory be garbage collected)
So what is happening here is whenever you run test(True) the first time, your myclass in module_x is effectively deleted and replaced with the myclass from module_a. All subsequent calls to test(False) then call global myclass which is effectively a no-op since the global myclass now refers to the one imported from the other class (and besides the global call is unneeded when not changing the global variable from a local scope as explained here).
To work around this, I would strongly suggest encapsulating the desired module-switching behavior in a class that is independent of either module you would like to switch. You can then charge that class with holding a reference to both modules and providing the rest of you client code with the correct one. E.g.
module_a_wrapper.py
import module_x
import module_a
class ModuleAWrapper(object):
_target_module = module_x # the default
#classmethod
def get_module(cls):
return cls._target_module
def set_module(enable_a):
if enable_a:
ModuleAWrapper._target_module = module_a
else:
ModuleAWrapper._target_module = module_x
def get_module():
return ModuleAWrapper.get_module()
main.py:
from module_a_wrapper import set_module, get_module
set_module(True)
get_module().myclass()
set_module(False)
get_module().myclass()
Running:
python main.py
# Outputs:
i'm myclass in module_a
i'm myclass in module_x
You can read more about the guts of the python import system here
The answer by lemonhead properly explains why this effect happens and gives a valid solution.
The general rule seems to be: wherever and however you import a module, it will always replace any variables of the same name from the global scope.
Funnily, when I use the import foo as bar construct, then there must neither be a global variable named foo nor one named bar!
So while lemonhead's solution worked it adds lots of complexity and will lead to my code being much longer because every time I want to get something from either module I have to prefix that call with the getter function.
This solution allows me to solve the problem with a minimal amount of changed code:
module_x.py:
class myclass_fast():
def __init__(self):
print("i'm myclass in module_x")
def test(enable_a):
if enable_a:
try:
from module_a import myclass
except ImportError:
enable_a = False
myclass = myclass_fast
else:
myclass = myclass_fast
i = myclass()
So the only thing I changed was to rename the class I had in global scope from myclass to myclass_fast. This way it will not be overwritten anymore by the import of myclass from module_a. Then, on demand, I change the local variable myclass to either be the imported module or myclass_fast.

Categories