I want to create a module with many attributes (in my case, SI units with prefixes, like mm, cm, km, ...). The obvious way would be to specify them one by one, like
# module.py
mm = "mm"
cm = "cm"
km = "km"
# and so on
Currently I want to have about 600 attributes of this kind, so to me it seems wrong to do it this way. Therefore I was wondering if it is possible to generate those attributes programatically instead. I start with a list of units and a list of prefixes and want to combine each unit with each prefix. As a simple example, consider the following:
# module.py
_prefixes = ["m", "c", "k"]
_names = ["m", "g"]
# Automate this
mm = "mm"
cm = "cm"
km = "km"
mg = "mg"
cg = "cg"
kg = "kg"
If I wanted those to be the attributes of a class, I could use setattr(class, prefix name, prefix name)
, but so far I did not find a way to translate this to module attributes.
I am aware that I could use a dict
or something similar instead of attributes, but I want to allow imports like from module import km
, which to my knowledge is only possible with direct attributes.
Is there a way to generate those attributes programatically? And if so, should I use it or define the attributes one by one nontheless (for example using a script generating the python code for me)?
CodePudding user response:
The attributes of a python module are anything you put in the global namespace regardless of whether it is done programatically or via manually typed code. You will often see the following statements used to add module attributes:
import
: A line likefrom itertools import product
will create a module attributeproduct
in your module.def
andclass
. These keywords create special types of objects whose names appear in the global namespace as well.- Regular assignments with
=
. Anything you assign outside a function or class body also becomes a module attribute.
If you want to generate a bunch of attribues, you can access the module __dict__
via globals()
:
from itertools import product
_prefixes = ["m", "c", "k"]
_names = ["m", "g"]
for prefix, name in product(_prefixes, _names):
globals()[prefix name] = prefix name
This module will have attributes product
, prefix
, name
, _prefixes
and _names
, which you may not want. You can clean them up with del
at the end of the code, when they are no longer necessary:
del product
del prefix
del name
All that being said, this is a terribly inefficient way of doing things. You likely don't need a separate attribute for each string. If you ever want to do any sort of manipulation or lookup on these units, or use them for something other than just "being there", you will want them in some sort of a data structure, like a list
or dict
.
Another item is that instead of generating or hard coding the names in your code, you might consider placing them in a configuration file that users can edit somewhere. The module would then contain code to locate and load the text file.
CodePudding user response:
So this option will not have any autocomplete, but in my IDE at least (Pycharm) it doesn't complain.
units.py
class Units:
prefixes = ["m", "c", "k"]
names = ["m", "g"]
def __init__(self, foo):
self.foo = foo
for p in self.prefixes:
for n in self.names:
setattr(self, p n, p n)
unit = Units(foo='bar')
Import the instance unit
in main.py
from units import unit
if __name__ == '__main__':
print(unit.mm)
print(unit.kg)
print(unit.cm)
print(unit.foo)
Output:
$ python main.py
mm
kg
cm
bar
CodePudding user response:
like this?
_prefixes = ["m", "c", "k"]
_names = ["m", "g"]
new_list = []
for i in _prefixes:
for a in _names:
new_list.append(i a)
Editing this again as I know what you mean. so the above makes a list then to create the variables you use globals as shown below.
for v in new_list:
globals()[f'{v}'] = v
print(km)
print(mm)