简体   繁体   中英

Global variables in Python through modules/file import

Problem :

I am trying to find a less clunky way to use (or accomplish something similar to) global variables. Right now I have all of my global variables in a file g.py , so I access them using g.var .

I would love to use var instead of g.var in my code, because I think it looks cleaner.

Details :

I have 3 files right now:

  • main.py : a small code for solving a PDE
  • functions.py : a file that defines functions for applying boundary conditions
  • g.py : a file that has the variables that are modified when functions from functions.py are called

g.py :

import numpy as np
# variables
ap  = np.float64(0.0)
awx = np.float64(0.0)
aex = np.float64(0.0)
rhs = np.float64(0.0)

functions.py :

import g

def bc_Neumann(i,m,nx):
    m[0]=int(i); m[1]=int(i-1); m[2]=int(i+1);
    if i==0:
        m[1]=nx-1
        g.ap=g.ap+g.awx
        g.awx=0.0
    if i==nx-1:
        m[2]=0
        g.ap=g.ap+g.aex
        g.aex=0.0
    return m

And main.py calls bc_Neumann() at some point.

Is there a better way to access g.ap , g.awx , etc.? I would like to just reference these global variables as ap , awx , etc.

You could import the variables directly, ie:

from g import ap, awx, aex, rhs

and then declare them as globals in your function (else they're considered locals and you'll get an UnboundLocalError when rebinding them):

def bc_Neumann(i,m,nx):
    global ap, awx, aex, rhs
    # your code here

BUT this won't update the g.ap , g.awx etc variables accordingly when you rebind ap , awx etc. The reason why is that by importing your variables that way you make the names local to your functions module, so rebinding them from within your function only affects the functions module namespace.

If that's not quite clear, think of module's scopes as dicts where the variable names are the keys. If you have two dicts A and B such as:

A = {"ap":[], "aw":[]}
B = {}

when in functions you do:

from g import ap, aw

it's as if you were doing

B["ap"] = A["ap"]
B["aw"] = A["aw"]

at this stage, keys in A and B refers to the same objects so if you mutate B["ap"] (ie by appending something to it), it will be seen in A too:

B["ap"].append(1)
print A["ap"]

BUT if instead you rebind B["ap"] to a new list, then A["ap"] won't be affected and B["ap"] and A["ap"] will now refer to two different objects:

B["ap"] = [42, 43]
print A["ap"]

FWIW, modules namespaces are exactly this: dicts.

So to make a long story short: this won't work as expected... So you'll either have to move all your variables to the same module as the functions using them (and declare them as globals in the functions using them) or live with g.ap etc.

This being said: global variables are a terrible idea , wherever they live. If you have a set of functions working on (mutating and rebinding) the same set of variables, you most often want to make the whole thing a class:

class Whatever(object):
    def __init__(self):
        self.ap  = np.float64(0.0)
        self.awx = np.float64(0.0)
        self.aex = np.float64(0.0)
        self.rhs = np.float64(0.0)

   def bc_neumann(self, i,m,nx):
      m[0] = int(i) 
      m[1] = int(i-1) 
      m[2] = int(i+1)
      if i == 0:
          m[1] = nx - 1
          self.ap = self.ap + self.awx
          self.awx = 0.0
      if i == nx-1:
          m[2] = 0
          self.ap = self.ap + self.aex
          self.aex = 0.0
      return m


w = Whatever()
w.bc_neumann(1, [], 42)
from g import ap, awx, aex, rhs
print(ap, awx, aex, rhs)

If you don't want to explicitly state the variable names, you can use from g import * , but this is generally not recommended. The reason is that explicitly stating the variable names makes it clear what variables are coming from where. If you said from g import * and from h import * and then started using some of their variables, it would be difficult to tell which comes from which without reading other files. Even when only importing from one file, it's good to know before reading the file (by reading the top of the file) what names come from somewhere else.

Edit: If you want to use this style but also want to be able to modify the values contained inside g , you need these variables to be mutable objects. You can do this with arrays.

g.py:

import numpy as np
# variables, dtype is float64 by default
ap = np.array([0.0])
awx = np.array([0.0])
aex = np.array([0.0])
rhs = np.array([0.0])

functions.py:

from g import ap, awx, aex, rhs

def bc_Neumann(i, m, nx):
    m[0] = int(i)
    m[1] = int(i-1)
    m[2] = int(i+1)
    if i == 0:
        m[1] = nx-1
        ap[0] = ap + awx
        awx[0] = 0.0
    if i == nx-1:
        m[2] = 0
        ap[0] = ap + aex
        aex[0] = 0.0
    return m

If you want ap , awx etc. to stay as an immutable object like a float, then your code should stay as-is, using eg g.ap .

Make your variables a part of a dictionary (Or SimpleNameSpace, if brackets bother you).

g.py

import numpy as np
# variables
G = dict(ap  = np.float64(0.0),
         awx = np.float64(0.0),
         aex = np.float64(0.0),
         rhs = np.float64(0.0))

Then in functions.py you can import it as

from g import G

Now you can access/update the variables as G['ap'], G['awx'] etc.

This works because Dictionaries and Lists are always treated as references.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM