1

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!

2
  • 1
    You experience here a good reason why most people refrain from using global variables: they are a pain, avoid them at all cost. If you do choose to use them, make sure you read all documentation about variable scope etc... Commented Mar 25, 2022 at 22:25
  • 2
    First, global doesn't do anything in a global scope. Second, Python doesn't have a process-wide namespace (aside from the built-in namespace), only per-module global namespaces. Commented Mar 25, 2022 at 22:25

4 Answers 4

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

It sounds a good explanation. Thanks! But thus, is there no way to avoid using params.config instead of simply config?
As I mentioned, main could update attributes on the existing object, so all referrers would see it regardless of whether they've assigned it to a different variable. You could add functions to update en mass if this is a large configuration. I think this would be a more common approach.
1

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())

Comments

-1

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.

4 Comments

That has its own problems if you really do need multiple instances of Config.
That's why the best answer is to not make a shared global state. :)
This is still global state; even worse, you are now sharing a value across multiple instances of Config as well as sharing an instance of Config across multiple modules.
Yes, I'm aware. I stated right above that that the best approach is to avoid this pattern altogether.
-2
#params.py:

config = []


#main.py:

from params import config

def mess_with_config():
    #global config         - this is
    #config= [['stuff']]   - wrong!
    
    config.append(['stuff'])  # ✔
   
mess_with_config()
print(config)

You can import the instance of config from another file. Using global followed by assignment will cause the variable to be overwritten locally, in which case it will no longer point to the same object you imported. This is why you should use the class methods instead.

Comments

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.