2

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)?

1
  • 1
    One option is to generate the required source code using a script, either using string manipulation or using the AST module. Commented Jul 15, 2022 at 0:24

3 Answers 3

1

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 like from itertools import product will create a module attribute product in your module.
  • def and class. 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.

Sign up to request clarification or add additional context in comments.

Comments

0

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)

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.