{"id":13398428,"url":"https://github.com/mewwts/addict","last_synced_at":"2025-10-04T20:38:25.505Z","repository":{"id":24405998,"uuid":"27806545","full_name":"mewwts/addict","owner":"mewwts","description":"The Python Dict that's better than heroin.","archived":false,"fork":false,"pushed_at":"2022-08-19T21:31:46.000Z","size":150,"stargazers_count":2517,"open_issues_count":25,"forks_count":132,"subscribers_count":57,"default_branch":"master","last_synced_at":"2025-04-10T03:44:49.800Z","etag":null,"topics":["addict","dict","dictionaries","python","python-dict","recurse"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mewwts.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-12-10T06:55:25.000Z","updated_at":"2025-04-07T12:03:00.000Z","dependencies_parsed_at":"2022-06-30T00:30:54.160Z","dependency_job_id":null,"html_url":"https://github.com/mewwts/addict","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mewwts%2Faddict","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mewwts%2Faddict/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mewwts%2Faddict/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mewwts%2Faddict/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mewwts","download_url":"https://codeload.github.com/mewwts/addict/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254044234,"owners_count":22005107,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["addict","dict","dictionaries","python","python-dict","recurse"],"created_at":"2024-07-30T19:00:25.621Z","updated_at":"2025-10-04T20:38:25.390Z","avatar_url":"https://github.com/mewwts.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# addict\n![Tests](https://github.com/mewwts/addict/workflows/Python%20test/badge.svg) [![Coverage Status](https://img.shields.io/coveralls/mewwts/addict.svg)](https://coveralls.io/r/mewwts/addict) [![PyPI version](https://badge.fury.io/py/addict.svg)](https://badge.fury.io/py/addict) [![Anaconda-Server Badge](https://anaconda.org/conda-forge/addict/badges/version.svg)](https://anaconda.org/conda-forge/addict)\n\naddict is a Python module that gives you dictionaries whose values are both gettable and settable using attributes, in addition to standard item-syntax.\n\nThis means that you **don't have to** write dictionaries like this anymore:\n```Python\nbody = {\n    'query': {\n        'filtered': {\n            'query': {\n                'match': {'description': 'addictive'}\n            },\n            'filter': {\n                'term': {'created_by': 'Mats'}\n            }\n        }\n    }\n}\n```\nInstead, you can simply write the following three lines:\n```Python\nbody = Dict()\nbody.query.filtered.query.match.description = 'addictive'\nbody.query.filtered.filter.term.created_by = 'Mats'\n```\n\n### Installing\nYou can install via `pip`\n```sh\npip install addict\n```\n\nor through `conda`\n```sh\nconda install addict -c conda-forge\n```\n\nAddict runs on Python 2 and Python 3, and every build is tested towards 2.7, 3.6 and 3.7. \n\n### Usage\naddict inherits from ```dict```, but is more flexible in terms of accessing and setting its values.\nWorking with dictionaries are now a *joy*! Setting the items of a nested Dict is a *dream*:\n\n```Python\n\u003e\u003e\u003e from addict import Dict\n\u003e\u003e\u003e mapping = Dict()\n\u003e\u003e\u003e mapping.a.b.c.d.e = 2\n\u003e\u003e\u003e mapping\n{'a': {'b': {'c': {'d': {'e': 2}}}}}\n```\n\nIf the `Dict` is instantiated with any iterable values, it will iterate through and clone these values, and turn `dict`s into `Dict`s.\nHence, the following works\n```Python\n\u003e\u003e\u003e mapping = {'a': [{'b': 3}, {'b': 3}]}\n\u003e\u003e\u003e dictionary = Dict(mapping)\n\u003e\u003e\u003e dictionary.a[0].b\n3\n```\nbut `mapping['a']` is no longer the same reference as `dictionary['a']`.\n```Python\n\u003e\u003e\u003e mapping['a'] is dictionary['a']\nFalse\n```\nThis behavior is limited to the constructor, and not when items are set using attribute or item syntax, references are untouched:\n```Python\n\u003e\u003e\u003e a = Dict()\n\u003e\u003e\u003e b = [1, 2, 3]\n\u003e\u003e\u003e a.b = b\n\u003e\u003e\u003e a.b is b\nTrue\n```\n\n### Stuff to keep in mind\nRemember that ```int```s are not valid attribute names, so keys of the dict that are not strings must be set/get with the get-/setitem syntax\n```Python\n\u003e\u003e\u003e addicted = Dict()\n\u003e\u003e\u003e addicted.a.b.c.d.e = 2\n\u003e\u003e\u003e addicted[2] = [1, 2, 3]\n{2: [1, 2, 3], 'a': {'b': {'c': {'d': {'e': 2}}}}}\n```\nHowever feel free to mix the two syntaxes:\n```Python\n\u003e\u003e\u003e addicted.a.b['c'].d.e\n2\n```\n\n### Attributes like keys, items etc.\naddict will not let you override attributes that are native to ```dict```, so the following will not work\n```Python\n\u003e\u003e\u003e mapping = Dict()\n\u003e\u003e\u003e mapping.keys = 2\nTraceback (most recent call last):\n  File \"\u003cstdin\u003e\", line 1, in \u003cmodule\u003e\n  File \"addict/addict.py\", line 53, in __setattr__\n    raise AttributeError(\"'Dict' object attribute '%s' is read-only\" % name)\nAttributeError: 'Dict' object attribute 'keys' is read-only\n```\nHowever, the following is fine\n```Python\n\u003e\u003e\u003e a = Dict()\n\u003e\u003e\u003e a['keys'] = 2\n\u003e\u003e\u003e a\n{'keys': 2}\n\u003e\u003e\u003e a['keys']\n2\n```\njust like a regular `dict`. There are no restrictions (other than what a regular dict imposes) regarding what keys you can use.\n\n### Default values\nFor keys that are not in the dictionary, addict behaves like ```defaultdict(Dict)```, so missing keys return an empty ```Dict```\nrather than raising ```KeyError```.\nIf this behaviour is not desired, it can be overridden using\n```Python\n\u003e\u003e\u003e class DictNoDefault(Dict):\n\u003e\u003e\u003e     def __missing__(self, key):\n\u003e\u003e\u003e         raise KeyError(key)\n```\nbut beware that you will then lose the shorthand assignment functionality (```addicted.a.b.c.d.e = 2```).\n\n### Recursive Fallback to dict\nIf you don't feel safe shipping your addict around to other modules, use the `to_dict()`-method, which returns a regular dict clone of the addict dictionary.\n\n```Python\n\u003e\u003e\u003e regular_dict = my_addict.to_dict()\n\u003e\u003e\u003e regular_dict.a = 2\nTraceback (most recent call last):\n  File \"\u003cstdin\u003e\", line 1, in \u003cmodule\u003e\nAttributeError: 'dict' object has no attribute 'a'\n```\nThis is perfect for when you wish to create a nested Dict in a few lines, and then ship it on to a different module. \n```Python\nbody = Dict()\nbody.query.filtered.query.match.description = 'addictive'\nbody.query.filtered.filter.term.created_by = 'Mats'\nthird_party_module.search(query=body.to_dict())\n```\n\n### Counting\n`Dict`'s ability to easily access and modify deeply-nested attributes makes it ideal for counting. This offers a distinct advantage over `collections.Counter`, as it will easily allow for counting by multiple levels.\n\nConsider this data:\n\n```python\ndata = [\n    {'born': 1980, 'gender': 'M', 'eyes': 'green'},\n    {'born': 1980, 'gender': 'F', 'eyes': 'green'},\n    {'born': 1980, 'gender': 'M', 'eyes': 'blue'},\n    {'born': 1980, 'gender': 'M', 'eyes': 'green'},\n    {'born': 1980, 'gender': 'M', 'eyes': 'green'},\n    {'born': 1980, 'gender': 'F', 'eyes': 'blue'},\n    {'born': 1981, 'gender': 'M', 'eyes': 'blue'},\n    {'born': 1981, 'gender': 'F', 'eyes': 'green'},\n    {'born': 1981, 'gender': 'M', 'eyes': 'blue'},\n    {'born': 1981, 'gender': 'F', 'eyes': 'blue'},\n    {'born': 1981, 'gender': 'M', 'eyes': 'green'},\n    {'born': 1981, 'gender': 'F', 'eyes': 'blue'}\n]\n```\n\nIf you want to count how many people were born in `born` of gender `gender` with `eyes` eyes, you can easily calculate this information:\n\n```python\ncounter = Dict()\n\nfor row in data:\n    born = row['born']\n    gender = row['gender']\n    eyes = row['eyes']\n\n    counter[born][gender][eyes] += 1\n\nprint(counter)\n```\n\n```\n{1980: {'M': {'blue': 1, 'green': 3}, 'F': {'blue': 1, 'green': 1}}, 1981: {'M': {'blue': 2, 'green': 1}, 'F': {'blue': 2, 'green': 1}}}\n```\n### Update\n`addict`s update functionality is altered for convenience from a normal `dict`. Where updating nested item using a `dict` would overwrite it:\n```Python\n\u003e\u003e\u003e d = {'a': {'b': 3}}\n\u003e\u003e\u003e d.update({'a': {'c': 4}})\n\u003e\u003e\u003e print(d)\n{'a': {'c': 4}}\n```\n`addict` will recurse and _actually_ update the nested `Dict`. \n```Python\n\u003e\u003e\u003e D = Dict({'a': {'b': 3}})\n\u003e\u003e\u003e D.update({'a': {'c': 4}})\n\u003e\u003e\u003e print(D)\n{'a': {'b': 3, 'c': 4}}\n```\n\n### When is this **especially** useful? \nThis module rose from the entirely tiresome creation of Elasticsearch queries in Python. Whenever you find yourself writing out dicts over multiple lines, just remember that you don't have to. Use *addict* instead.\n\n### Perks\nAs it is a ```dict```, it will serialize into JSON perfectly, and with the to_dict()-method you can feel safe shipping your addict anywhere.\n\n### Testing, Development and CI\nIssues and Pull Requests are more than welcome. Feel free to open an issue to spark a discussion around a feature or a bug, or simply reply to the existing ones. As for Pull Requests, keeping in touch with the surrounding code style will be appreciated, and as such, writing tests are crucial. Pull requests and commits will be automatically run against TravisCI and coveralls. \n\nThe unit tests are implemented in the `test_addict.py` file and use the unittest python framework. Running the tests is rather simple:\n```sh\npython -m unittest -v test_addict\n\n# - or -\npython test_addict.py\n```\n\n### Testimonials\n@spiritsack - *\"Mother of God, this changes everything.\"*\n\n@some guy on Hacker News - *\"...the purpose itself is grossly unpythonic\"*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmewwts%2Faddict","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmewwts%2Faddict","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmewwts%2Faddict/lists"}