1

I need to create a mapping of keys (strings, all suitable python identifiers) to values in python (3.9). All keys and values are constant and known at creation time and i want to make sure, that every single key has an associated value.

1. dict

The first idea that comes to mind for this would be using a dictionary, which comes with the big problem that keys (in my case) would be strings.

That means i have to retype the key each time a value is accessed manually in a string literal, so IDEs and type checkers can't spot typos, suggest key names in autocomplete and i can't use their utility functions to rename or find usages of a key.

1.5 dict with constant variable keys

the naive solution for this would be to create a constant for each key or an enum, which i don't think is a good solution. Not only is at least one name-lookup added to each access, it also means that the key definition and the value assignment are separated, which can lead to keys that don't have a value assigned to them.

2. enum

This leads to the idea to skip the dict and use an enum to associate the keys directly with the values. Enums are conveniently supported by syntax-checkers, auto completion an the likes, as they support both attribute reference via "dot-notation" and subscriptions via "[]".

However an enum has the big disadvantage that it requires all keys/Enum-Members to have unique values and keys violating this rule will automatically be converted to aliases which makes outputs very confusing.

I already thought about copying the Enum-Code and removing the unwanted bits, but this seems to be a lot of effort for such a basic problem.

question:

So basically, what i'm looking for is a pythonic, neat and concise way to define a (potentially immutable) mapping from string keys to arbitrary values which supports the following:

  • iterable (over keys)
  • keys with identical values don't interfere with each other
  • keys are required to have an associated value
  • keys are considered by syntax-checkers, auto-completion, refactorings, etc.

The preferred way of using it would be to define it in a python source file but it would be a nice bonus, if the solution supported easy means to write the data to a text file (json format, or ini or similar) and to create a new instance from such a file.

How would you do that and why would you choose a specific solution?

Sorontik
  • 69
  • 1
  • 6

1 Answers1

1

For the first part, I would use aenum1, which has a noalias setting (so duplicate values can exist with distinct names):

from aenum import NoAliasEnum

class Unqiue(NoAliasEnum):
    first = 1
    one = 1

and in use:

>>> Unique.first
<Unique.first: 1>

>>> Unique.one
<Unique.one: 1>

>>> # name lookup still works
>>> Unique['one']
<Unique.one: 1>

>>> # but value lookups do not
>>> Unique(1)
Traceback (most recent call last):
  ...
TypeError: NoAlias enumerations cannot be looked up by value

For the second part, decide which you want:

  • read and create enums from a file
  • create enum in Python and write to a file

Doing both doesn't seem to make a lot of sense.

To create from a file you can use my JSONEnumMeta answer.

To write to a file you can use my share enums with arduino answer (after adapting the __init_subclass__ code).

The only thing I'm not certain of is the last point of syntax-checker and auto-completion support.


1 Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
  • Since it's a normal class and the Members default class members, most coding tools should support this out of the box – Sorontik Dec 24 '21 at 22:00
  • Thank you for this answer @EthanFurman . Unfortunately, with this approach i have to use `Unique.first.value` to access the actual value which i don't like at all. so for now i decided to simply use a module with module-wide variables which can be iterated using the builtin `vars()` on the module and skipping anything starting and ending with an underscore. I know this could be considered abuse, but of all the solutions i know of, it's the one i like best (at least until i stumble upon new difficulties) – Sorontik Dec 25 '21 at 19:25