6 ms·
Here's a long-running question I have; please tell me if I'm confused / missing something obvious. Say I want to model in Python some collection of key-value p
by da39a3ee 3y ago
Here's a long-running question I have; please tell me if I'm confused / missing something obvious.
Say I want to model in Python some collection of key-value pairs. It's a singleton -- I don't need a class or namedtuple that can be instantiated. So for example, I just want something like:
SingletonDataContainer Config:
root_directory: str = "/tmp"
should_frobnicate: bool = False
So of course, it could be a dict. But I want type annotations, and dotted attribute access syntax (and pretty syntax-highlighting!). It's tempting to use a class with class attributes for this, but that seems wrong since then you end up with something that can be instantiated, but which you never actually want to instantiate.
- gabereiser 3y agoJust put them in a module as variables. Import the module named and you have them as “dotted attributes”. Python doesn’t need all this OOP stuff like structs when we have dataclasses and attrs, modules and dicts, that can represent draconian C structs.
- Spivak 3y agoAs well as modules being natural singletons you can also make your class an ABC to stop instantiation. A module variable set to a SimpleNamespace in the typing package is also an option.
- da39a3ee 3y agoThanks. I wonder what it would be like if Python had syntax for modules instead of them being tied to separate files. I would prefer being able to create this container in the same file as other code.
- Spivak 3y agoSay no more! Python? Rules? Unheard of. from importlib.machinery import ModuleSpec import importlib.util import sys spec = ModuleSpec("mymodule", loader=None) module = importlib.util.module_from_spec(spec) module.__dict__.update({"a": 42}) sys.modules["mymodule"] = module import mymodule print(mymodule.a) # -> 42 That's if you want a proper module. But modules can be any type and it's officially supported! import sys from types import SimpleNamespace sys.modules["mymodule"] = SimpleNamespace(b=37) import mymodule print(mymodule.b) # -> 37 # ^^^ This is literally just calling __getattr__ on the module object. Do with that what you will. This will cross files as well but you need to make sure the code to create the module runs before you try importing it! glhf
- da39a3ee 3y agoThanks! This was a great answer. > Python? Rules? Unheard of. OK, since it's Christmas, I'd like to be able to write Module MyModule a = 42 b = 37
- Spivak 3y agoI think I can do that. import sys class DataModule: def __init_subclass__(cls): sys.modules[cls.__name__.lower()] = cls class MyModle(DataModule): a = 42 b = 37 import mymodule mymodule.a == 42 Or as a class decorator def datamodule(cls): sys.modules[cls.__name.__.lower()] = cls @datamodule class MyModule: a = 42 import mymodule mymodule.a = 42
- mitchellpkt 3y agoCould use a class, but access the value without ever instantiating it? Not sure if this fits the bill for what you want, or if the fact that it could theoretically be instantiated renders the approach non-suitable. class Config: root_directory: str = "/tmp" should_frobnicate: bool = False if __name__=="__main__": print(f"{Config.root_directory=}\n{Config.should_frobnicate=}")
- xapata 3y agoWill you ever worry about thread safety for these values? If not, then as others have mentioned, a `config` module seems like what you want.
- sarahdellysse 3y agoSo basically you want something like how typescript does, where you can do this: const config = { rootDirectory: "/tmp", shouldFrobnicate: false, } I'm far from a python expert, more like a TS dev who occasionally tries to write Python, but when I'm in that situation I find that writing one-off classes seems to be python's way of doing things. I'm not a super big fan of it myself, but it works: from dataclasses import dataclass def _create_config(): @dataclass(kw_only=True) class Config: root_directory: str = "/tmp" should_frobnicate: bool = False return Config() config = _create_config() I experimented with making a decorator that could do something like this, but never got it working with `mypy` so I gave up: @iife def config(): @dataclass(kw_only=True) class Config: root_directory: str = "/tmp" should_frobnicate: bool = False return Config() the `@iife` would make it so that `config` was a variable, not a `def`, but yeah the whole thing stank so I gave up