How to Programmatically Set a Global (Module) Variable

How to programmatically set a global (module) variable?

You can set globals in the dictionary returned by globals():

definitions = {'a': 1, 'b': 2, 'c': 123.4}
for name, value in definitions.items():
globals()[name] = value

How to create module-wide variables in Python?

Here is what is going on.

First, the only global variables Python really has are module-scoped variables. You cannot make a variable that is truly global; all you can do is make a variable in a particular scope. (If you make a variable inside the Python interpreter, and then import other modules, your variable is in the outermost scope and thus global within your Python session.)

All you have to do to make a module-global variable is just assign to a name.

Imagine a file called foo.py, containing this single line:

X = 1

Now imagine you import it.

import foo
print(foo.X) # prints 1

However, let's suppose you want to use one of your module-scope variables as a global inside a function, as in your example. Python's default is to assume that function variables are local. You simply add a global declaration in your function, before you try to use the global.

def initDB(name):
global __DBNAME__ # add this line!
if __DBNAME__ is None: # see notes below; explicit test for None
__DBNAME__ = name
else:
raise RuntimeError("Database name has already been set.")

By the way, for this example, the simple if not __DBNAME__ test is adequate, because any string value other than an empty string will evaluate true, so any actual database name will evaluate true. But for variables that might contain a number value that might be 0, you can't just say if not variablename; in that case, you should explicitly test for None using the is operator. I modified the example to add an explicit None test. The explicit test for None is never wrong, so I default to using it.

Finally, as others have noted on this page, two leading underscores signals to Python that you want the variable to be "private" to the module. If you ever do an import * from mymodule, Python will not import names with two leading underscores into your name space. But if you just do a simple import mymodule and then say dir(mymodule) you will see the "private" variables in the list, and if you explicitly refer to mymodule.__DBNAME__ Python won't care, it will just let you refer to it. The double leading underscores are a major clue to users of your module that you don't want them rebinding that name to some value of their own.

It is considered best practice in Python not to do import *, but to minimize the coupling and maximize explicitness by either using mymodule.something or by explicitly doing an import like from mymodule import something.

EDIT: If, for some reason, you need to do something like this in a very old version of Python that doesn't have the global keyword, there is an easy workaround. Instead of setting a module global variable directly, use a mutable type at the module global level, and store your values inside it.

In your functions, the global variable name will be read-only; you won't be able to rebind the actual global variable name. (If you assign to that variable name inside your function it will only affect the local variable name inside the function.) But you can use that local variable name to access the actual global object, and store data inside it.

You can use a list but your code will be ugly:

__DBNAME__ = [None] # use length-1 list as a mutable

# later, in code:
if __DBNAME__[0] is None:
__DBNAME__[0] = name

A dict is better. But the most convenient is a class instance, and you can just use a trivial class:

class Box:
pass

__m = Box() # m will contain all module-level values
__m.dbname = None # database name global in module

# later, in code:
if __m.dbname is None:
__m.dbname = name

(You don't really need to capitalize the database name variable.)

I like the syntactic sugar of just using __m.dbname rather than __m["DBNAME"]; it seems the most convenient solution in my opinion. But the dict solution works fine also.

With a dict you can use any hashable value as a key, but when you are happy with names that are valid identifiers, you can use a trivial class like Box in the above.

How to change a module variable from another module?

You are using from bar import a. a becomes a symbol in the global scope of the importing module (or whatever scope the import statement occurs in).

When you assign a new value to a, you are just changing which value a points too, not the actual value. Try to import bar.py directly with import bar in __init__.py and conduct your experiment there by setting bar.a = 1. This way, you will actually be modifying bar.__dict__['a'] which is the 'real' value of a in this context.

It's a little convoluted with three layers but bar.a = 1 changes the value of a in the module called bar that is actually derived from __init__.py. It does not change the value of a that foobar sees because foobar lives in the actual file bar.py. You could set bar.bar.a if you wanted to change that.

This is one of the dangers of using the from foo import bar form of the import statement: it splits bar into two symbols, one visible globally from within foo which starts off pointing to the original value and a different symbol visible in the scope where the import statement is executed. Changing a where a symbol points doesn't change the value that it pointed too.

This sort of stuff is a killer when trying to reload a module from the interactive interpreter.

Access modules and module variables at runtime from python via String

EDIT

The OP is actually using micropython, which doesn't implement importlib...

Possible (untested) solution (yes, using exec - if someone knows a better solution please chime in).

def import_module(name):
name = name.strip().split(
statement = "import {}"
exec(statement, globals())
return sys.modules[name]

class Writer(object):
def __init__(self):
# reference to the current module
# WARNING : this is the `module` object itself,
# not it's name
self.current_module = None

# {name:module} mapping
# not sure it's still useful since we
# now have a reference to the module itself
# and `sys.modules` already caches imported
# modules...
self.imported_fonts = {}

# set default font
self.setfont("arial", "14")

def setfont(self, fontname, fontsize):
self.current_module = self._loadfont(fontname, fontsize)

def _loadfont(self, fontname, fontsize):
name = fontname+str(fontsize)
if name not in self.imported_fonts:
self.imported_fonts[name] = load_module(name)
return self.imported_font[name]

def putc_ascii(self, ch, xpos, ypos):
width, height, bitmap = self._get_char(ch)
print("{}.{}: {} - {} - {}".format(
self.current_module, ch, width, height, bitmap
)

def _get_char(self, chrnum):
# assume the font modules have been rewritten
# using dicts (cf lower) and all chars are defined
# in all fonts modules
return self.current_module[ch]

# alternate implementation using the current
# fonts definitions
# return getattr(self.current_module, "ch_{}".format(ch))

TL:DR :

You want importlib.import_module and eventually getattr(). But you should still read the longer answer, really, it will save you a lot of time and frustration.

Longer answer:

First point about your "font" files format - this:

ch_33 = 3, 16, [0,0,0,0,0,0,0,0,0,1,1,1,1,1 ........
ch_34 = 5, 16, [0,0,0,0,0,0,0,0,0,0,0,0,0,0 ........

is a huge design smell. You want lists or dicts instead (or possibly an ordered dict), ie:

characters = {
33: (3, 16, [0,0,0,0,0,0,0,0,0,1,1,1,1,1...]),
34: (5, 16, [0,0,0,0,0,0,0,0,0,0,0,0,0,0...]),
# etc
}

As a general rule, when you start having some "var1", "var2", "var3" etc pattern then you know you want some kind of container instead.

Second point - your error handling, ie:

 try:
global arial14
import arial14
self.current_module = "arial14"
self.imported_fonts = []
self.imported_fonds.append(self.current_module)
except ImportError:
print("Error loading Font")

is worse than useless, it's actually harmful. First because it doesn't stop program execution in the case of an unrecoverable error (you dont want your program to continue when something totally wrong happened), then because it replaces all the incredibly useful informations you'd either get from the error message and traceback with a plain useless "An error happened" message.

Only catch exceptions that you can properly manage one way or another, let everything else propagate (maybe someone upper in the call stack - usually in the UI part - might be able to handle it properly).

Third point: do not use globals. I mean: do not mutate nor rebind globals (read-only (pseudo-constant) globals are ok of course). Which means you really never have to use the "global" keyword in your code.

When you need to share state between a group a functions, use a class, store the state as attributes and make your functions methods. That's what objects are for (well, not only but that's part of there raison d'être).

EDIT : this part is still true for complete Python implementation and probably still partly true for micropython, except for parts that are not implemented in micropython (like importlib - I don't know what else is missing)

And finally: never ever use exec nor eval. You don't need them, whatever you're trying to do there's a better, safer specific solution.

In your case, to import a module by it's name, you have importlib.import_module, and to get an object's attribute by it's name you have getattr() (but if you use a proper container, cf first point, you don't even need getattr here).

Insert variable into global namespace from within a function?

Yes, just use the global statement.

def func():
global var
var = "stuff"

How to make global imports from a function?

Imported modules are just variables - names bound to some values. So all you need is to import them and make them global with global keyword.

Example:

>>> math
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'math' is not defined
>>> def f():
... global math
... import math
...
>>> f()
>>> math
<module 'math' from '/usr/local/lib/python2.6/lib-dynload/math.so'>

Programmatically creating variables in Python

from string import uppercase
_g = globals()
for char in uppercase:
_g[char] = Variable(char)

Whether this is a good idea remains questionable :)

This only works for globals() (i.e. module level assignments), as the language definition explicitly states that modifying the dictionary returned by locals() may not actually change the value of any local variables.

You can also do something similar with the __dict__ of a class or instance.

How do I call setattr() on the current module?

import sys

thismodule = sys.modules[__name__]

setattr(thismodule, name, value)

or, without using setattr (which breaks the letter of the question but satisfies the same practical purposes;-):

globals()[name] = value

Note: at module scope, the latter is equivalent to:

vars()[name] = value

which is a bit more concise, but doesn't work from within a function (vars() gives the variables of the scope it's called at: the module's variables when called at global scope, and then it's OK to use it R/W, but the function's variables when called in a function, and then it must be treated as R/O -- the Python online docs can be a bit confusing about this specific distinction).



Related Topics



Leave a reply



Submit