Hacker News new | past | comments | ask | show | jobs | submit login
DotDict: A simple Python library to make chained attributes possible (github.com/nick-at-dave)
44 points by astrea on Aug 2, 2023 | hide | past | favorite | 53 comments



In case anyone is looking for mature alternatives that are used in production

https://pypi.org/project/python-box/

https://pypi.org/project/munch/


Neither of these appear to have the chained attribute feature, from my surface level parse.


python-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: {}>


It'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


Chained means in your second example this would work without manually creating a new instance:

    b.foo.lol = True



How does these compare to glom?

https://glom.readthedocs.io


Munch is an unfortunate name


My 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.


And 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.


100%. I don’t understand the obsession with bending dicts into something they are inherently bad at when actual alternatives in dataclasses and Pydantic models right there.


Can dataclasses be serialized just as easily?


In addition to the already-mentioned suggestions, you can also use marshmallow schemas to serialise complex objects, and there's even a package to autogen marshmallow schemas from dataclasses: https://pypi.org/project/marshmallow-dataclass/


Yep! Both msgspec (https://jcristharif.com/msgspec/supported-types.html#datacla...) and orjson support encoding dataclasses to JSON natively.


Yes via pickle.

Or for JSON, dataclasses_json


Be aware that pickle is unsafe (as documented near the top of https://docs.python.org/3/library/pickle.html), so prefer other serialization formats except between trusted processes


I 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!


I use pickle to serialize RPC requests between internal services. Nothing beats it in terms of speed and ease of use.


Does 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.


Yeah 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.


They could add a __bool__ method to the class the returns false when that level of the namespace is empty. That would allow:

   if a.b.c:
       print("c:", a.b.c)


The __contains__ method should be used (and indeed is). What if a.b.c = False?


I don't think it's possible in Python to detect the difference between access or write (when it's nested attributes).


Code looks like it's on access, but I didn't test it.


This 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.


Could have been even easier: `argparse.Namespace` already has `__contains__()` ツ

That said, https://pypi.org/project/addict/ has been around a long time...


That 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.


I 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.



Wow, 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 :)


This is horrifying and yet I can’t look away.


That is a work of art, and also you have to appreciate the author’s commitment to the bit.


I feel bad that no one seemed to get the joke. At the same time, that makes the whole thing funnier.


So basically Perl autovivification for Python?


Seems 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 } ] )


This seems similar to the functionality exposed by Box.

https://github.com/cdgriffith/Box/wiki/Types-of-Boxes#defaul...



Anthony’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


Here's my personal finger exercise: https://pypi.org/project/talamus-relatable/

It allows references to another data structures.


this 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/6844917fb51e9d24de1...

2. https://github.com/nathants/py-util/blob/6844917fb51e9d24de1...


I also created an alternative to this, a while back:

https://pypi.org/project/jsane/

Its syntax is a bit uglier, though.


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


Why 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.


This 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.


Oh, I thought this was going to be like ChainMap, but for attributes, I'll stick with the one ChatGPT started for me.


This is the way. And that pip command is just clean.


Have I lost my marbles? It doesn't work?

    (temp5)   ~/temp5 pip install .            
    ERROR: Directory '.' is not installable. Neither 'setup.py' nor 'pyproject.toml' found.


Run 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).


As per this comment, I added it to pypi. You can now just do ‘pip install attr-dot-dict’.


I...do not prefer this syntax.


edict() is an interesting name for a dictionary function.


It’s a class instantiation with no arguments


Yeah, I was referring to the name choice. Edict being “A decree or proclamation issued by an authority and having the force of law.”




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: