Python - Importing module gets global variables - python

I have two Python scripts, one testclass.py:
import numpy
zz = numpy
class Something(object):
def __init__(self):
self.xp = zz
and one testscript.py:
from testclass import Something
x = Something()
print(x.xp)
I expected testscript.py to throw an error because I thought that testscript only imports the class Something (with its __init__ method), and not the global variable zz. So, given this bevahiour, my question is, when importing from a module, does Python "run" everything in the module file?

Yes. When you execute:
from testclass import Something
It has the same effect as:
import testclass
Something = testclass.Something
More generally, the Python interpreter can't know beforehand what objects your module exposes (unless you explicitly name them in __all__). For an extreme case, consider the following:
a.py:
import random
if random.random() > 0.5:
class Foo(object):
pass
else:
class Bar(object):
pass
Running from a import Foo has a 50% chance of failing because the a module object may or may not have a Foo attribute.

Related

Is there a way to monkey patch a class inside a module before the module body is executed in python?

I have file main.py which contains the following code:
class A:
def __init__(self, a):
self.a = a
def run(self):
return self.a+10
a = A(4)
print(a.run())
In file test.py, I tried to monkey patch class A in main.py as follows:
import main
class A:
def __init__(self, a):
self.a = a
def run(self):
return self.a+5
main.A = A
Unfortunately, when I run import test from a python interpreter, the module still prints out 14 as opposed to my expected output which is 9.
Is there a way to monkey patch a class inside a module before the module body is executed?
The problem here is that when you imported the main.py file, it executed the code a = A(4) using the real implementation of the class A. Then the rest of your test.py was executed and you replaced the A reference, but it was too late.
You can check that by adding in your test :
print(__name__) # __main__
print(main.__name__) # so70731368_main
print(A.__module__) # __main__
print(main.A.__module__) # __main__
print(main.a.__class__.__module__) # so70731368_main
Here, __main__ is a bit confusing but that's how Python call the first file you run (in your case test.py). The a instance is declared in the so70731368_main module, and it used the A class from the same module, you just changed A after the fact with the definition from the test file (__main__).
The fact that you need to patch two definitions (A and a) defined in the same file is very tricky. unittest.mock.patch is not powerful enough to patch inside an import (it patches after the import).
You can not, in a clean and simple way, prevent a to be instantiated as a main.A (real) class and get printed. What you can do is patch it after, for later uses, that is what you showed.
To answer directly your question : "patching" means replacing one reference by another, so the reference has to already be defined. In your example, it would require to patch between the class definition and the class instantiation (for the print to not use the real a), which is not supported.
There is no simple solution to this problem. If you have control over the code of the main.py file, then try to change it so that it does not instantiate a at import time.

Inherit from a class not imported yet?

I have a modules.py file defining several base classes.
One of these base classe inherit itself from a class defined in another module:
class MyClass(torch.nn.Module):
...
However, I'd prefer not to import torch in that modules.py file yet, because it's quite a big library, and this modules.py file is used by multiple processes, which would mean all of these processes would also have to import torch, leading quickly to an overflow (been there, done that).
Is there a way to define MyClass and specify it's based on torch.nn.Module without having to import torch just yet, and let the processes who really need the torch module do the import themselves ? So the other processes not needing MyClass could just ignore it's there and not try to resolve it, for instance ?
There is a rather advanced way to simulate dynamic import by building a dummy class and importing the real module and overwriting its attributes on first call of its __new__ special method.
Demo:
module A.py:
class Attempt:
_changed = False
def __new__(cls, i):
if not cls._changed:
cls._changed = True
import B
for name, member in B.B.__dict__.items():
if name not in ('__dict__', '__module__'):
setattr(cls, name, member)
return object.__new__(cls)
module B.py:
print('importing module B')
class B:
a = 5
def __init__(self, i):
self.i = i
def __repr__(self):
return f'B({self.i})'
You can then use it that way:
>>> import A
>>> a = A.Attempt(2)
importing module B
>>> b = A.Attempt(3)
>>> print(a,b)
B(2) B(3)
Which proves that the module B is only loaded at creation of the first object.

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.

Python: importing another .py file

I have a class and I want to import a def function by doing:
import <file>
but when I try to call it, it says that the def can not be found. I also tried:
from <file> import <def>
but then it says global name 'x' is not defined.
So how can I do this?
Edit:
Here is a example of what I am trying to do. In file1.py I have:
var = "hi"
class a:
def __init__(self):
self.b()
import file2
a()
and in file2.py I have:
def b(self):
print(var)
it is just giving me a error though.
import file2
loads the module file2 and binds it to the name file2 in the current namespace. b from file2 is available as file2.b, not b, so it isn't recognized as a method. You could fix it with
from file2 import b
which would load the module and assign the b function from that module to the name b. I wouldn't recommend it, though. Import file2 at top level and define a method that delegates to file2.b, or define a mixin superclass you can inherit from if you frequently need to use the same methods in unrelated classes. Importing a function to use it as a method is confusing, and it breaks if the function you're trying to use is implemented in C.

Python, faking module in Class definition

I suspect this is one of those questions that will result in "Why do you want to do that?" but here goes:
I need to find a way to fake the module of a class defined in __main__; ie:
Lets say we have a.py:
class A(object):
name = "a"
pass
And b.py:
import inspect
import a
class B(object):
__name__ = a.__name__
name = "b"
pass
print inspect.getmodule(B)
Calling b.py results in:
<module '__main__' from 'b.py'>
So, how do I get the module of class A taken to be b, without moving it into b ?
You can set the class's __module__ attribute, but there are caveats:
class A(object):
__module__ = 'b'
The main caveat is that this will cause getmodule to return None if the module b has not imported at the time you call getmodule(A). I think it's also possible for problems to arise if you import b in some nonstandard way, like importing it via an implicit relative import instead of as part of a package (if it's part of a package).
Conceptually, though, what you're doing is somewhat dubious. It's risky to set A's module to be some other module, because that module might not be known (i.e., imported) when the real module is, and in fact it's possible the "disguise" module might not even exist.

Categories