Effect of Submodules on Monkeypatching of Classes in Python - python

I have three modules:
in_mod.py
class IN(object):
def __init__(self):
print("i am the original IN")
module1.py
from in_mod import IN
class C(object):
def __init__(self):
cl = IN()
and module2.py
from module1 import C
class IN2(object):
def __init__(self):
print("I am the new IN")
C()
import in_mod
in_mod.IN = IN2
C()
import module1
module1.IN = IN2
C()
I get the desired behaviour of monkey-patching out the IN class and replacing it with the IN2 class when I use module1.IN = IN2.
I would like to understand what the underlying difference between in_mod.IN = IN2 and module1.IN = IN2 is in this context.
I am referencing a related post.

from module import IN creates a local variable that references module.IN; module.IN and IN are two separate references to the same class. IN = IN2 changes the local reference, but module.IN (which is used by module.C) continues to reference the same class.
UPDATE
In your edit, module1.IN is a global variable in the module1 namespace that initially refers to a class in in_mod, but is distinct from the global in_mod.IN in the module namespace. Changing its value doesn't affect in_mod at all. There's no way to directly access in_mod's namespace via module1, because you don't import the entire module, just one value from the module.

Related

Share global variables between Python scripts

I have 3 scripts: params.py (it defines a configuration class), foo.py (it uses that configuration) and main.py (it initializes the configuration and calls foo).
params.py:
class Config:
def __init__(self, x=0):
self.val = x
global config
config = Config()
foo.py:
from params import config
def foo():
return config.val + 5
main.py:
from params import config
from foo import foo
config = Config(10)
print(foo())
But instead of print 15, it prints 5. How can I fix it? It occurs because when foo.py does the import, it initializes config with 0. But, what can I do to modify from main the config value and read the new value from all other scripts?
Thank you!
Conceptually, you need to separate an object like Config() from the variables that may be referencing it at any given time. When params.py does config = Config(), it creates a Config object and assigns it to a variable in the params module namespace. It is params.config.
When main.py does from params import config, it adds a reference to this Config object to its own namespace. Now there are two references to the same object, one in params.config and another in main.config. So far, so good. from X import Y adds a binding to X.Y into the current namespace. Since params.config is a mutable class instance, main could change the values in that single Config object and it would be seen by all other referrers to that same object. config.val = 10 would be seen by all.
Now things go off the rails. When main does config = Config(10), it creates a new Config object and reassigns that variable to the main namespace. Now params.config references the first object and main references the second. That means changes made to the second object are not seen by the first.
If you want everyone to see the same object, you need to keep the namespace qualification. The scripts would change to
foo.py:
import params
def foo():
return params.config.val + 5
main.py:
import params
from foo import foo
params.config = Config(10)
print(foo())
Now, all of the scripts are using the one variable params.config and see any changes made to that object. This is kindof fragile as you've seen. If anybody does from params import config, reassiging params.config doesn't work.
global only marks a name in a local scope as being global; it has no affect in a global scope, in that it is already global.
What you want isn't really possible, as global namespaces are specific to an individual module, not the process as a whole.
If the value is defined in params.py, you will need to access it via params from all other modules, include the __main__ module created by your script.
params.py:
class Config:
def __init__(self, x=0):
self.val = x
config = Config()
foo.py:
import params
def foo():
return params.config.val + 5
main.py:
import params
from foo import foo
params.config = params.Config(10)
print(foo())
If you simply modified the existing configuration, you could use
params.py (same as above):
class Config:
def __init__(self, x=0):
self.val = x
config = Config()
foo.py (same as your original foo.py):
from params import config
def foo():
return config.val + 5
main.py
from params import config
from foo import foo
config.val = 10
print(foo())
In general, I don't think this is a good idea, as you're essentially creating a global state that can change from any file that imports the configuration file. This is known as action at a distance.
The best answer is to avoid this pattern altogether. For example, come up with a way to use the configuration file in a read-only manner.
That being said, if you really want to do this, make the variable class-level rather than instance-level, so that there exists only one val shared across the entire program.
class Config:
val = 0
def __init__(self, x=0):
Config.val = x
global config
config = Config()
Then, running main.py will print 15.

Python global variable not found when using OOP

Now I have the problem with the global variable that defined but when run the class object, it raises the error of "The 'variable' is not defined
Specifically,
I have created a file called __init__.py which contains global variables
Create the class_0.py, contain the function which uses defined variable in __init__.py
Create the class_1.py, to combine things which also run the function in class_0.py
__init__.py
asset = {'abc': 123, ...}
cost = {...}
class_0.py
from __init__ import *
class defc():
def function(self):
a = asset['abc']
return ...
class_1.py
from __init__ import *
from class_0 import *
class run_main():
def __init__(self):
self.defc = defc()
def run(self):
self.defc.function()
Then it raises the Error
NameError: name 'asset' is not defined
So how to solve this problem? the asset variable is the very long dictionary and I don't want to put it in a specific class due to there still other class use this.
Save all your work and try restarting your kernel.

Sharing a global object across files with circular dependencies

I have a global object which uses a class, and that class uses the global object. How can i put the imports correct in a clean way
I now have:
run.py (The file i run)
from global_class import Global_class
global_object = Global_class()
global_object.create_some_object()
global_class.py
from some_class import Some_class
class Global_class:
def __init__(self):
self.name = 'my_name'
def create_some_object(self):
self.some_object = Some_class()
some_class.py
class Some_class:
def __init__(self):
print(global_object.name)
How can i now access global_object in Some_class? If i put:
from run import global_object
It creates a circular dependency, and it crashes. A possible method i thought of was putting the some_class import in the Global_class::create_some_object() method, but it seems to me as unclean code. Is there any better way
Any python import module or from module import Class statement runs corresponding module line by line and loads all the objects in the module namespace in the memory. However each of the names in the modules reside separately (that is the purpose of the modules after all). So a global_object in some_class.py is completely separate from the global_object in run.py. When the interpreter sees this name in some_class.py it will look in the local and global namespace (using the LEGB rule which stands for local, enclosed, global and builtins). But there is no reference to global_object exists here, it does in the calling module. Your suggestion of putting the some_class import statement inside the method create_object() will also not work for the same reason. As you have found out you cannot import global_object in some_class as it will again need to run the run.py thus creating a loop.
If you want to maintain this setup then one option would be to explicitly pass the global_object which is self in Global_class to the Some_class() constructor like below
#run.py
from global_class import Global_class
global_object = Global_class()
global_object.create_some_object()
#global_class.py
from some_class import Some_class
class Global_class:
def __init__(self):
self.name = 'my_name'
def create_some_object(self):
self.some_object = Some_class(self) #self -> global_object
#some_class.py
class Some_class:
def __init__(self, global_object):
print(global_object.name)
$ python run.py
my_name

Inheriting a class with the same name in Python

I am new in Python and I am trying to create two classes with the same name in two different source files. Let’s call them "Main.py" and "Extension.py". The class is "MyClass". MyClass in Extesntion.py is derived from MyClass in file Main.py. If it works then when I create an object myclass and I import Extension in my code, then I would have more functions in comparison with file Main.py.
File Main.py
class MyClass:
def __init__(self):
Initialize something
def foo1(self, a, b):
Do something
Then extension would be like this:
File Extensions.py
import Main
class MyClass(MyClass):
def __init__(self):
Initialize something
def foo2(self, a, b):
Do something
def foo3(self, a, b):
Do something
And then if I have code like this. I expect that I can't use foo2 and foo3.
import Main
myclass = MyClass()
myclass.foo1(a, b)
And finally if I have code like this. I expect that I use all the functions.
import Extension
myclass = MyClass()
myclass.foo1(a, b)
myclass.foo2(a, b)
myclass.foo3(a, b)
If you do
import main
you'll need to use main.MyClass to create an object from main.py.
Instead you can do
from main import MyClass
to have it available directly.
If you need two different classes with the same name, you can instead do
from main import MyClass as MainClass
and you'll have the class available under the name MainClass
Unless you do from Extension import *, you'll need to specify the module in order to access the class.
import Main
import Extension
foo = Main.MyClass()
bar = Extension.MyClass()
If you don't want to have to specify the module, then the only way to avoid a name collision is to import the class under a different name like so:
from Main import MyClass as ClassA
It's quite easy when you explicitly import the given name using the from {module} import {name} syntax.
File main.py
class MyClass:
def __init__(self):
pass
def foo1(self, a, b):
pass
File extensions.py
from main import MyClass
class MyClass(MyClass):
def __init__(self):
pass
def foo2(self, a, b):
pass
def foo3(self, a, b):
pass
File client_main.py
from main import MyClass
myinstance = MyClass()
myinstance.foo1(a, b)
File client_extensions.py
from extensions import MyClass
myinstance = MyClass()
myinstance.foo1(a, b)
myinstance.foo2(a, b)
myinstance.foo3(a, b)
Generally in this case, you would do an import as. This allows you to alias your import as a new name. So in the file where your second class is, import the first class as:
from main import MyClass as MainMyClass
Then when doing your inheritance, refer to MainMyClass:
class MyClass(MainMyClass):

how to change a variable from a module inside a module in a module... (confused)

I have a variable in my main module which is changed using another module, but I want to change the variable from my main module through another module. I'm new to programmering, so I actually don't really know how to explain this stuff - sorry if I'm asking a stupid question.
The program's hierarchy looks a bit like this:
Main
---Features
---Pygame_handling
------Features
I use the "Features" module to change a variable in the "Main". I do this simply by getting the defined variable from "Features". But when I change the variable through "Pygame_handling", it is not changed in the "Features" object created in the "Main" module.
Main.py
import Features
class Simulator:
def __init__(self):
self.Features = Features.Methods()
self.variables = self.Features.dictionary
self.PyObject = Pygame_handling.Window()
Pygame_handling.py
import Features
class Window:
def __init__(self):
self.Features = Features.Methods()
dict = {"some":"dict"}
self.Features.monitor_changes(dict)
How are you initializing those classes?
Usually when I need to do something like this I code it like:
py1.py:
class Test(object):
def test_print(self):
print 'Hi!'
TEST = Test()
py2.py:
from py1 import TEST
TEST.test_print()
# Adding new stuff to the TEST initialized class
TEST.new_var = 50
print TEST.new_var
#output: 50
So now you can just use the initialized class from on that module.

Categories

Resources