320

I'm bit confused about how the global variables work. I have a large project, with around 50 files, and I need to define global variables for all those files.

What I did was define them in my projects main.py file, as following:

# ../myproject/main.py

# Define global myList
global myList
myList = []

# Imports
import subfile

# Do something
subfile.stuff()
print(myList[0])

I'm trying to use myList in subfile.py, as following

# ../myproject/subfile.py

# Save "hey" into myList
def stuff():
    globals()["myList"].append("hey")

An other way I tried, but didn't work either

# ../myproject/main.py

# Import globfile    
import globfile

# Save myList into globfile
globfile.myList = []

# Import subfile
import subfile

# Do something
subfile.stuff()
print(globfile.myList[0])

And inside subfile.py I had this:

# ../myproject/subfile.py

# Import globfile
import globfile

# Save "hey" into myList
def stuff():
    globfile.myList.append("hey")

But again, it didn't work. How should I implement this? I understand that it cannot work like that, when the two files don't really know each other (well subfile doesn't know main), but I can't think of how to do it, without using io writing or pickle, which I don't want to do.

martineau
  • 119,623
  • 25
  • 170
  • 301
  • @rodion: importing cycles - the code in subfile tries to import globfile, which in itś body imports itself back – jsbueno Oct 23 '12 at 16:21

10 Answers10

466

The problem is you defined myList from main.py, but subfile.py needs to use it. Here is a clean way to solve this problem: move all globals to a file, I call this file settings.py. This file is responsible for defining globals and initializing them:

# settings.py

def init():
    global myList
    myList = []

Next, your subfile can import globals:

# subfile.py

import settings

def stuff():
    settings.myList.append('hey')

Note that subfile does not call init()— that task belongs to main.py:

# main.py

import settings
import subfile

settings.init()          # Call only once
subfile.stuff()         # Do stuff with global var
print settings.myList[0] # Check the result

This way, you achieve your objective while avoid initializing global variables more than once.

Hai Vu
  • 37,849
  • 11
  • 66
  • 93
  • 58
    I like the general approach, but not the whole `init()` stuff. Modules are only evaluated the first time they're imported, so it's perfectly OK to initialize those variables in the body of the module. – Kirk Strauser Oct 23 '12 at 17:10
  • 27
    +1 Kirk: I agree. However, my approach prevent the case where other modules modify globals.myList before the main program starts. – Hai Vu Oct 23 '12 at 20:52
  • 3
    You should call it something other than globals, which is a builtin name. PyLint gives the warning: "Redefining built-in 'globals' (redefined-builtin)" – twasbrillig Jan 27 '15 at 20:29
  • Thanks. Any idea how to remove the “Undefined variable from import” errors that appear in Eclipse PyDev by using this file structure (i.e. importing global variables from settings.py)? I had to [disable the error in PyDev](http://stackoverflow.com/a/32111638/395857), which is not ideal. – Franck Dernoncourt Aug 20 '15 at 07:08
  • @FranckDernoncourt I am sorry, I don't use Eclipse so I am even more clueless than you are. – Hai Vu Aug 20 '15 at 12:10
  • but what if i want to use myList as an interactive variable cross the files, and save the last changes in setting.py for further usage again?! – Mohsen Haddadi Jun 03 '18 at 06:19
  • @MohsenHaddadi then you need to write a file anyways, so just do that at the end and read it in init(). Look at "shelve" for example. – DonQuiKong Nov 15 '18 at 12:50
  • When the subfile.py program already contains statement import settings.py, why do we need to import settings in main.py again? – Nikhil Valiveti Sep 19 '20 at 10:11
  • Is `setting.` still your preferred prefix? My initial thought was `config.` prefix but I'm thinking that is too long. Also all my global variables are currently in `UPPERCASE `and I was thinking of changing to `lower_case`. My instinct is `CamelCase` but pycharm may not like that. I kind of like `g.` for global variables because it is shorert to type. Any thoughts? – WinEunuuchs2Unix Jul 06 '21 at 23:01
  • My issue with this approach is that it is brittle to edits of the global variable. For instance, if you edit the global variable in `subfile1`, it may not be propagated to `subfile2` – information_interchange Oct 26 '21 at 15:45
  • 1
    Yeah I also get a strong sense this whole approach is patchwork. Better make variables that you pass between your functions or have common classes/objects otherwise. It's a software engineering issue if better tools aren't utilized for the same purpose. – j riv Feb 06 '22 at 12:13
  • would this depend on how settings.py is imported ? – Alex Kreimer Mar 08 '22 at 12:55
  • the problem is how to update global values across the different scripts, I tried but if one script update the global values, other scripts still do not know it – adameye2020 Dec 10 '22 at 17:31
186

See Python's document on sharing global variables across modules:

The canonical way to share information across modules within a single program is to create a special module (often called config or cfg).

config.py:

x = 0   # Default value of the 'x' configuration setting

Import the config module in all modules of your application; the module then becomes available as a global name.

main.py:

import config
print (config.x)

In general, don’t use from modulename import *. Doing so clutters the importer’s namespace, and makes it much harder for linters to detect undefined names.

Ogaga Uzoh
  • 2,037
  • 1
  • 10
  • 12
  • 11
    This seems like a cleaner approach than the accepted answer. – JoeyC Jan 23 '19 at 00:48
  • 28
    Note that you cannot set `x` using `from config import x`, only using `import config` – Yariv Nov 20 '19 at 07:21
  • I think this answer is partially incorrect. This seems to only be guaranteed to work with the first import syntax. The linked documentation also only has the first syntax and not the `from config import x`. When using the `from` syntax things become much more complicated and the order of import matters quite a bit. – Mark Rucker Oct 28 '20 at 19:50
  • I confirm `from config import x` doesn't work as comment from @Yariv states. Also you are importing a **lower case** `x` and printing and **upper case** `X` which also won't work. Still the link is wonderful :) – WinEunuuchs2Unix Jul 06 '21 at 23:51
  • Where do you put the config.py file, relative to other python packages? – NullPumpkinException Nov 23 '22 at 23:53
24

You can think of Python global variables as "module" variables - and as such they are much more useful than the traditional "global variables" from C.

A global variable is actually defined in a module's __dict__ and can be accessed from outside that module as a module attribute.

So, in your example:

# ../myproject/main.py

# Define global myList
# global myList  - there is no "global" declaration at module level. Just inside
# function and methods
myList = []

# Imports
import subfile

# Do something
subfile.stuff()
print(myList[0])

And:

# ../myproject/subfile.py

# Save "hey" into myList
def stuff():
     # You have to make the module main available for the 
     # code here.
     # Placing the import inside the function body will
     # usually avoid import cycles - 
     # unless you happen to call this function from 
     # either main or subfile's body (i.e. not from inside a function or method)
     import main
     main.mylist.append("hey")
Morgoth
  • 4,935
  • 8
  • 40
  • 66
jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • 2
    wow, normally one would expect two files importing each other to get into an infinite loop. – Nikhil VJ Aug 24 '18 at 04:11
  • 3
    ha At first glance it looks that way, doesn't it? What happens in the def stuff() is that the import doesn't run when the file loads..it only runs when the stuff() function is called. So starting with main we import subfile and then call subfile.stuff() which then imports main...no loop, just import once in main. See the note in the subfile.py example about import cycles. – John Apr 18 '19 at 18:59
  • It looks like it doesn't work anymore: "most likely due to a circular import", as expected at first glance (Python 3.9.5) – Yuri Khristich Jun 27 '21 at 16:31
12

Using from your_file import * should fix your problems. It defines everything so that it is globally available (with the exception of local variables in the imports of course).

for example:

##test.py:

from pytest import *

print hello_world

and:

##pytest.py

hello_world="hello world!"
IT Ninja
  • 6,174
  • 10
  • 42
  • 65
  • 4
    Except if you assign to one such variable – jsbueno Oct 23 '12 at 16:03
  • 5
    I personally avoid the use of `import *` at all cost so that references are explicit (and not confusing), Besides, when ever have you actually used all "`*`" references in any module? – ThorSummoner Sep 18 '14 at 20:30
  • 30
    DON'T DO import *. Your global variables will no longer remain in sync. Each module receives its own copy. Changing the variable in one file will not reflect in another. It is also warned against in https://docs.python.org/2/faq/programming.html#how-do-i-share-global-variables-across-modules – Isa Hassen Feb 07 '16 at 14:11
10

Hai Vu answer works great, just one comment:

In case you are using the global in other module and you want to set the global dynamically, pay attention to import the other modules after you set the global variables, for example:

# settings.py
def init(arg):
    global myList
    myList = []
    mylist.append(arg)


# subfile.py
import settings

def print():
    settings.myList[0]


# main.py
import settings
settings.init("1st")     # global init before used in other imported modules
                         # Or else they will be undefined

import subfile    
subfile.print()          # global usage
lastboy
  • 566
  • 4
  • 12
4

Your 2nd attempt will work perfectly, and is actually a really good way to handle variable names that you want to have available globally. But you have a name error in the last line. Here is how it should be:

# ../myproject/main.py

# Import globfile    
import globfile

# Save myList into globfile
globfile.myList = []

# Import subfile
import subfile

# Do something
subfile.stuff()
print(globfile.myList[0])

See the last line? myList is an attr of globfile, not subfile. This will work as you want.

Mike

MikeHunter
  • 4,144
  • 1
  • 19
  • 14
2

I just came across this post and thought of posting my solution, just in case of anyone being in the same situation as me, where there are quite some files in the developed program, and you don't have the time to think through the whole import sequence of your modules (if you didn't think of that properly right from the start, such as I did).

In such cases, in the script where you initiate your global(s), simply code a class which says like:

class My_Globals:
  def __init__(self):
    self.global1 = "initial_value_1"
    self.global2 = "initial_value_2"
    ...

and then use, instead of the line in the script where you initiated your globals, instead of

global1 = "initial_value_1"

use

globals = My_Globals()

I was then able to retrieve / change the values of any of these globals via

globals.desired_global

in any script, and these changes were automatically also applied to all the other scripts using them. All worked now, by using the exact same import statements which previously failed, due to the problems mentioned in this post / discussion here. I simply thought of global object's properties being changing dynamically without the need of considering / changing any import logic, in comparison to simple importing of global variables, and that definitely was the quickest and easiest (for later access) approach to solve this kind of problem for me.

DevelJoe
  • 856
  • 1
  • 10
  • 24
2

Based on above answers and links within I created a new module called global_variables.py:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# ==============================================================================
#
#       global_variables.py - Global variables shared by all modules.
#
# ==============================================================================

USER = None                 # User ID, Name, GUID varies by platform

def init():
    """ This should only be called once by the main module
        Child modules will inherit values. For example if they contain
        
            import global_variables as g
            
        Later on they can reference 'g.USER' to get the user ID.
    """
    global USER

    import getpass
    USER = getpass.getuser()

# End of global_variables.py

Then in my main module I use this:

import global_variables as g
g.init()

In another child imported module I can use:

import global_variables as g
# hundreds of lines later....
print(g.USER)

I've only spent a few minutes testing in two different python multiple-module programs but so far it's working perfectly.

WinEunuuchs2Unix
  • 1,801
  • 1
  • 17
  • 34
2

Namespace nightmares arise when you do from config import mySharedThing. That can't be stressed enough.

It's OK to use from in other places.

You can even have a config module that's totally empty.

# my_config.py
pass
# my_other_module.py
import my_config

def doSomething():
    print(my_config.mySharedThing.message)
# main.py
from dataclasses import dataclass
from my_other_module import doSomething
import my_config

@dataclass
class Thing:
    message: str

my_config.mySharedThing = Thing('Hey everybody!')
doSomething()

result:

$ python3 main.py
Hey everybody!

But using objects you pulled in with from will take you down a path of frustration.

# my_other_module.py
from my_config import mySharedThing

def doSomething():
    print(mySharedThing.message)

result:

$ python3 main.py
ImportError: cannot import name 'mySharedThing' from 'my_config' (my_config.py)

And maybe you'll try to fix it like this:

# my_config.py
mySharedThing = None

result:

$ python3 main.py
AttributeError: 'NoneType' object has no attribute 'message'

And then maybe you'll find this page and try to solve it by adding an init() method.

But the whole problem is the from.

qel
  • 944
  • 7
  • 7
0

I saw once somewhere in the internet (sorry, can't remember where) the following way (that I have been using since then):

  1. Create a file called (for example) variables.py where you declare all your globals, ex:

    variable1=0.0 variable2= "my text"

  2. call such variables wherever you want (in another files for example):

    variables.variable1= 1 (here assigning 1 to it)

Edit: found an example of it: https://www.edureka.co/community/52900/how-do-i-share-global-variables-across-modules-python