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
How to Efficiently Handle European Decimal Separators Using the Pandas Read_CSV Function
Timedelta to String Type in Pandas Dataframe
Replace Negative Values in an Numpy Array
How to Let a Raw_Input Repeat Until I Want to Quit
Python Pandas Group by Date Using Datetime Data
How to Convert Escaped Characters
Sort Multidimensional Array Based on 2Nd Element of the Subarray
Attributeerror: Can Only Use .Dt Accessor with Datetimelike Values
Python Runtimewarning: Overflow Encountered in Long Scalars
How to Normalize JSON Correctly by Python Pandas
When Should Iteritems() Be Used Instead of Items()
Finding Duplicate Files and Removing Them
What Do All the Distributions Available in Scipy.Stats Look Like
Pycharm Import External Library