The problem with these styles of library is that their modularity is relatively poor. Once you start using attribute access for simple data, or relying on extended features of the container, all your callers, and future reuses of the code are forced to also use that style of access.
While it is easy to mock up a container sporting attributes that can be substituted in place of the instance type, e.g.:
class Bag(dict):
__setattribute__ = dict.__setitem__
__getattr__ = dict.__getitem__
Another problem will rear its ugly head: namespace collisions, since the field names of 'user data' with attribute names of the container type cannot conflict with any method you might (now, or most likely in the future, once you've already dug the hole) want to add to the instance type.For these reasons, mantra 3 of 'import this' comes to mind:
Simple is better than complex.
Why not just use a dict, or something that looks almost identical to a dict, to begin with? Caller code is not polluted with your types, interoperability with things that expect dicts is ensured, and future extension is imminently possible since 'user fields' cannot conflict with methods you may wish to add.I've recently picked up DataKit work again. The local development version has a dict-like object whose only extension is to ensure the type of assigned items matches that of the schema type. This replaces an old 'Entity' class that required attribute access (essentially chosen only because it was "prettier").
A hidden benefit of passing a simple type is easier testability: from a unit test, initializing a class with a dict literal is much easier than needing some attribute-like stub class that must be declared and initialized first (to avoid that, you're forced to test your container class at the same time as whatever caller code is currently under test, which is a broken habit).
As if it wasn't clear: please, please continue passing naked dictionaries around, they're a universal container protocol evolved over 20+ years that wasn't in need of fixing.