10 ms·
DotDict: A simple Python library to make chained attributes possible
- i13e 3y agoGood video on this subject: https://www.youtube.com/watch?v=nDd2WVb_Enw https://www.youtube.com/watch?v=nDd2WVb_Enw
- b33j0r 3y agoAnthony’s video 100%. For anyone who doesn’t watch… the point being made is that this has been done so many times that yet another implementation was pulled from the stdlib in 3.12 (AttrDict). It’s true. I’ve implemented this several times myself, and thought it was a new/good idea every time until I remembered how many abstractions it breaks if I use it anywhere. I can’t promise I won’t do it again in a few weeks! `NaturalDictLite` is probably coming around in the accompanying rotation of bad name ideas :p
- dancenow 3y agoThis is the way. And that pip command is just clean.
- Toxygene 3y agoHave I lost my marbles? It doesn't work? (temp5) ~/temp5 pip install . ERROR: Directory '.' is not installable. Neither 'setup.py' nor 'pyproject.toml' found.
- Izkata 3y agoRun it from a clone of the repo to install it to the system / your user. I don't see a name to install this from pypi (I see several dotdict libraries on there but they don't link here, so don't know if this is there somewhere).
- astrea 3y agoAs per this comment, I added it to pypi. You can now just do ‘pip install attr-dot-dict’.
- kshay 3y agoSo basically Perl autovivification for Python?
- sekia 3y agoSeems a subset of it. Practicality aside, Perl can autovivify not only hashes but also arrays: my %x; $x{foo}[1]{bar} = 42; # %x is ( foo => [ undef, { bar => 42 } ] )
- astrea 3y agoThis is more of a political statement than a proper library. More to demonstrate the flexibility of Python given I was able to so easily do this.
- nicwolff 3y agoCould have been even easier: `argparse.Namespace` already has `__contains__()` ツ That said, https://pypi.org/project/addict/ https://pypi.org/project/addict/ has been around a long time...
- astrea 3y agoThat contains doesn’t permit the behavior, though. It would give you an AttributeError if you attempted this. Yes, addict seems to be exactly what I was going for.
- Waterluvian 3y agoI enjoy how much complete control Python enables. It’s fun to turn it into what resembles a different language. I want to make a library where you never ever call functions with fn() notation. But just utilize accessors and absolutely bastardize dicts.
- richbell 3y agoMy favorite example of this: https://old.reddit.com/r/learnprogramming/comments/kegalv/my_first_python_script_coming_from_c_feedback/ https://old.reddit.com/r/learnprogramming/comments/kegalv/my...
- Waterluvian 3y agoThis is horrifying and yet I can’t look away.
- necovek 3y agoWow, great job by this "beginner" just "learning a language"! Demonstrates knowledge of so many of Python intricacies that it's worth starting a Non-obfuscated Obfuscated Python Code Contest :)
- gorgoiler 3y agoDoes it lazy-create leaf dicts on access? It would be preferable if they were only created on assignment, and failed if it’s just a get. For example, if I call config.usors.adminn.naem I’d like it to fail loud and fast to point out the typos.
- Izkata 3y agoCode looks like it's on access, but I didn't test it.
- kzrdude 3y agoI don't think it's possible in Python to detect the difference between access or write (when it's nested attributes).
- heisenzombie 3y agoYeah it does. If you do: x = dd() print(x.tyypo.a.b) you'll get "DotDict()" The problem is that an object can't know whether you're getting a node that you're later going to assign a leaf to... or whether you're getting the node because you want to get the node. x = dd() x.a.b.c = "Foo" is equivalent to: x = dd() y = x.a.b y.c = "Foo" So "x.a.b" can't raise an exception. Now, it's python, so you can do unholy things to make pretty much anything work. But I think it would be ugly.
- sidmitra 3y agoIn case anyone is looking for mature alternatives that are used in production https://pypi.org/project/python-box/ https://pypi.org/project/python-box/ https://pypi.org/project/munch/ https://pypi.org/project/munch/
- Halvedat 3y agoNeither of these appear to have the chained attribute feature, from my surface level parse.
- jperras 3y agopython-box does this in the most intuitive way possible: >>> from box import Box >>> empty_box = Box(default_box=True) >>> empty_box.a.b.c.d.e.f.g # <Box: {}>
- nmalaguti 3y agoBox can do it with https://github.com/cdgriffith/Box/wiki/Types-of-Boxes#default-box https://github.com/cdgriffith/Box/wiki/Types-of-Boxes#defaul...
- sidmitra 3y agoIt's there on the readme/pypi home for both; unless i'm misunderstanding what you mean by "chained" from box import Box movie_box = Box({ "Robin Hood: Men in Tights": { "imdb stars": 6.7, "length": 104 } }) movie_box.Robin_Hood_Men_in_Tights.imdb_stars from munch import Munch b = Munch() b.hello = 'world' b.foo = Munch(lol=True) b.foo.lol
- Izkata 3y agoChained means in your second example this would work without manually creating a new instance: b.foo.lol = True
- make3 3y agoMunch is an unfortunate name
- nathants 3y agothis is cool! i use a lot of defaultdict trees[1], and convert nested dicts to/from flat dicts with dots in their keys[2]. never underestimate the mighty dict. 1. https://github.com/nathants/py-util/blob/6844917fb51e9d24de130fd4728053d62c7946da/util/dicts.py#L92 https://github.com/nathants/py-util/blob/6844917fb51e9d24de1... 2. https://github.com/nathants/py-util/blob/6844917fb51e9d24de130fd4728053d62c7946da/tests/test_dicts.py#L95 https://github.com/nathants/py-util/blob/6844917fb51e9d24de1...
- bbstats 3y agoI...do not prefer this syntax.
- hk1337 3y agoedict() is an interesting name for a dictionary function.
- nmalaguti 3y agoThis seems similar to the functionality exposed by Box. https://github.com/cdgriffith/Box/wiki/Types-of-Boxes#default-box https://github.com/cdgriffith/Box/wiki/Types-of-Boxes#defaul...
- claytonjy 3y agoMy general rule is, if I have a dict with a fixed set of keys, it should be a dataclass. Problem solved! Then if I need to send or receive it, it's an easy conversion to a Pydantic BaseModel.
- smashed 3y agoAnd the dataclass serves as documentation for the available attributes and type safety.. It's really the way to go, takes a minute or two to define and saves so much headaches in the long run.
- behnamoh 3y agoCan dataclasses be serialized just as easily?
- paulddraper 3y agoYes via pickle. Or for JSON, dataclasses_json
- dharmab 3y agoBe aware that pickle is unsafe (as documented near the top of https://docs.python.org/3/library/pickle.html https://docs.python.org/3/library/pickle.html), so prefer other serialization formats except between trusted processes
- claytonjy 3y agoI can't remember the last time I _wanted_ to pickle something. Parquet and JSON pretty much cover it for me. I guess pickling made more sense before tools like Pydantic were good and popular? If I want all the python-specific nuance on disk, I know I'm in a bad place!
- nurettin 3y agoI use pickle to serialize RPC requests between internal services. Nothing beats it in terms of speed and ease of use.
- stavros 3y agoI also created an alternative to this, a while back: https://pypi.org/project/jsane/ https://pypi.org/project/jsane/ Its syntax is a bit uglier, though.
- bee_rider 3y agoIf I do config = dd() config.users.foo = 'bar' config.users = ‘baz’ Is config.users turned into a string and config.users.foo is lost? Or is the equal symbol mapped to something cleverer than assignment?
- fatso784 3y agoThis is a pretty cool idea. One comment —the print representation should be entirely akin to a dict, just braces. No DotDict wrapper. That’d be cool.
- stuaxo 3y agoOh, I thought this was going to be like ChainMap, but for attributes, I'll stick with the one ChatGPT started for me.
- barelysapient 3y agoWhy not use Pydantic or Dataclasses instead? At least with Pydantic, there's the discipline to define the model and validation that a model instance is probably correct.
- tniemi 3y agoHere's my personal finger exercise: https://pypi.org/project/talamus-relatable/ https://pypi.org/project/talamus-relatable/ It allows references to another data structures.