<div align="center">
<h1>MetaDict</h1>
_Enabling dot notation and IDE autocompletion_
<p align="center">
<a href="#installation">Installation</a> •
<a href="#features">Features</a> •
<a href="#documentation">Documentation</a> •
<a href="#competitors">Competitors</a> •
<a href="#citation">Citation</a>
</p>
[](https://www.python.org/downloads/release/python-360/)
[](https://badge.fury.io/py/metadict)
[](https://circleci.com/gh/LarsHill/metadict/tree/main)
[](https://codecov.io/gh/LarsHill/metadict)
[](https://opensource.org/licenses/Apache-2.0)
</div>
---
**MetaDict** is designed to behave exactly like a `dict` while enabling (nested) attribute-style key access/assignment with IDE autocompletion support.
Many libraries claim to do the same, but fail in different ways (see <a href="#competitors">Competitors</a>).
## Installation
```bash
$ pip install metadict
```
## Features
- Attribute-style key access and assignment (dot notation) with IDE autocompletion support
```python
from metadict import MetaDict
cfg = MetaDict()
cfg.optimizer = 'Adam'
print(cfg.optimizer)
>> Adam
```

- Nested key assignment similar to `defaultdict` from `collections`
```python
cfg = MetaDict(nested_assignment=True)
cfg.model.type = 'Transformer'
print(cfg.model.type)
>> Transformer
# or restrict nested assignment via context manager
cfg = MetaDict()
with cfg.enabling_nested_assignment() as cfg:
cfg.model.type = 'Transformer'
cfg.new_model.type = 'MLP'
>> AttributeError: 'MetaDict' object has no attribute 'new_model'
```
- Is a `dict`
```python
dict_config = {'model': 'Transformer',
'optimizer': 'Adam'}
cfg = MetaDict(dict_config)
print(isinstance(cfg, dict))
>> True
print(cfg == dict_config)
>> True
```
- Inbuilt `json` support
```python
import json
cfg = MetaDict({'model': 'Transformer'})
print(json.loads(json.dumps(cfg)))
>> {'model': 'Transformer'}
```
- Recursive conversion to `dict`
```python
cfg = MetaDict({'models': [{'name': 'Transformer'}, {'name': 'MLP'}]})
print(cfg.models[0].name)
>> Transformer
cfg_dict = cfg.to_dict()
print(type(cfg_dict['models'][0]))
>> <class 'dict'>
# Note: Appending a `dict` to a list within a `MetaDict` does not convert the `dict`.
# MetaDict does not overwrite `list` so intercepting `append`. `extend`, etc. is currently not possible.
# Simply wrap the appended or extended `dict` as a `MetaDict`.
cfg.models.append({'name': 'RNN'})
print(isinstance(cfg.models[-1], MetaDict))
>> False
cfg.models.append(MetaDict({'name': 'RNN'}))
print(isinstance(cfg.models[-1], MetaDict))
>> True
```
- No namespace conflicts with inbuilt methods like `items()`, `update()`, etc.
```python
cfg = MetaDict()
# Key 'items' is assigned as in a normal dict, but a UserWarning is raised
cfg.items = [1, 2, 3]
>> UserWarning: 'MetaDict' object uses 'items' internally. 'items' can only be accessed via `obj['items']`.
print(cfg)
>> {'items': [1, 2, 3]}
print(cfg['items'])
>> [1, 2, 3]
# But the items method is not overwritten!
print(cfg.items)
>> <bound method Mapping.items of {'items': [1, 2, 3]}>
print(list(cfg.items()))
>> [('items', [1, 2, 3])]
```
- References are preserved
```python
params = [1, 2, 3]
cfg = MetaDict({'params': params})
print(cfg.params is params)
>> True
model_dict = {'params': params}
cfg = MetaDict(model=model_dict)
print(cfg.model.params is params)
>> True
# Note: dicts are recursively converted to MetaDicts, thus...
print(cfg.model is model_dict)
>> False
print(cfg.model == model_dict)
>> True
```
## Documentation
Check the [Test Cases](https://github.com/LarsHill/metadict/blob/main/tests/test_metadict.py) for a complete overview of all **MetaDict** features.
## Competitors
- [Addict](https://github.com/mewwts/addict)
- No key autocompletion in IDE
- Nested key assignment cannot be turned off
- Newly assigned `dict` objects are not converted to support attribute-style key access
- Shadows inbuilt type `Dict`
- [Prodict](https://github.com/ramazanpolat/prodict)
- No key autocompletion in IDE without defining a static schema (similar to `dataclass`)
- No recursive conversion of `dict` objects when embedded in `list` or other inbuilt iterables
- [AttrDict](https://github.com/bcj/AttrDict)
- No key autocompletion in IDE
- Converts `list` objects to `tuple` behind the scenes
- [Munch](https://github.com/Infinidat/munch)
- Inbuilt methods like `items()`, `update()`, etc. can be overwritten with `obj.items = [1, 2, 3]`
- No recursive conversion of `dict` objects when embedded in `list` or other inbuilt iterables
- [EasyDict](https://github.com/makinacorpus/easydict)
- Only strings are valid keys, but `dict` accepts all hashable objects as keys
- Inbuilt methods like `items()`, `update()`, etc. can be overwritten with `obj.items = [1, 2, 3]`
- Inbuilt methods don't behave as expected: `obj.pop('unknown_key', None)` raises an `AttributeError`
## Citation
```
@article{metadict,
title = {MetaDict - Enabling dot notation and IDE autocompletion},
author = {Hillebrand, Lars},
year = {2022},
publisher = {GitHub},
journal = {GitHub repository},
howpublished = {\url{https://github.com/LarsHill/metadict}},
}
```
Raw data
{
"_id": null,
"home_page": "https://github.com/LarsHill/metadict/",
"name": "metadict",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.6",
"maintainer_email": null,
"keywords": "dict, attribute-style syntax, nesting, auto-completion",
"author": "Lars Hillebrand",
"author_email": "hokage555@web.de",
"download_url": "https://files.pythonhosted.org/packages/d1/e1/b1831cf30286259e8250c9ec2814cc27c1110a91437723f85fb5b74138ab/metadict-0.1.5.tar.gz",
"platform": null,
"description": "<div align=\"center\">\n<h1>MetaDict</h1>\n\n_Enabling dot notation and IDE autocompletion_\n\n<p align=\"center\">\n<a href=\"#installation\">Installation</a> \u2022\n <a href=\"#features\">Features</a> \u2022\n<a href=\"#documentation\">Documentation</a> \u2022\n <a href=\"#competitors\">Competitors</a> \u2022\n <a href=\"#citation\">Citation</a>\n</p>\n\n[](https://www.python.org/downloads/release/python-360/)\n[](https://badge.fury.io/py/metadict)\n[](https://circleci.com/gh/LarsHill/metadict/tree/main)\n[](https://codecov.io/gh/LarsHill/metadict)\n[](https://opensource.org/licenses/Apache-2.0)\n\n</div>\n\n---\n\n**MetaDict** is designed to behave exactly like a `dict` while enabling (nested) attribute-style key access/assignment with IDE autocompletion support. \n\nMany libraries claim to do the same, but fail in different ways (see <a href=\"#competitors\">Competitors</a>). \n\n## Installation\n\n```bash\n$ pip install metadict\n```\n## Features\n\n- Attribute-style key access and assignment (dot notation) with IDE autocompletion support\n ```python\n from metadict import MetaDict\n \n cfg = MetaDict()\n cfg.optimizer = 'Adam'\n print(cfg.optimizer)\n >> Adam\n ```\n \n- Nested key assignment similar to `defaultdict` from `collections`\n ```python\n cfg = MetaDict(nested_assignment=True)\n cfg.model.type = 'Transformer' \n print(cfg.model.type)\n >> Transformer\n \n # or restrict nested assignment via context manager\n cfg = MetaDict()\n with cfg.enabling_nested_assignment() as cfg:\n cfg.model.type = 'Transformer'\n cfg.new_model.type = 'MLP'\n >> AttributeError: 'MetaDict' object has no attribute 'new_model'\n ```\n- Is a `dict`\n ```python\n dict_config = {'model': 'Transformer',\n 'optimizer': 'Adam'} \n cfg = MetaDict(dict_config)\n print(isinstance(cfg, dict))\n >> True\n print(cfg == dict_config)\n >> True\n ```\n- Inbuilt `json` support\n ```python\n import json\n \n cfg = MetaDict({'model': 'Transformer'})\n print(json.loads(json.dumps(cfg)))\n >> {'model': 'Transformer'}\n ```\n- Recursive conversion to `dict`\n ```python \n cfg = MetaDict({'models': [{'name': 'Transformer'}, {'name': 'MLP'}]})\n print(cfg.models[0].name)\n >> Transformer\n \n cfg_dict = cfg.to_dict()\n print(type(cfg_dict['models'][0]))\n >> <class 'dict'>\n \n # Note: Appending a `dict` to a list within a `MetaDict` does not convert the `dict`.\n # MetaDict does not overwrite `list` so intercepting `append`. `extend`, etc. is currently not possible.\n # Simply wrap the appended or extended `dict` as a `MetaDict`.\n cfg.models.append({'name': 'RNN'})\n print(isinstance(cfg.models[-1], MetaDict))\n >> False\n \n cfg.models.append(MetaDict({'name': 'RNN'}))\n print(isinstance(cfg.models[-1], MetaDict))\n >> True\n ```\n- No namespace conflicts with inbuilt methods like `items()`, `update()`, etc.\n ```python \n cfg = MetaDict()\n # Key 'items' is assigned as in a normal dict, but a UserWarning is raised\n cfg.items = [1, 2, 3]\n >> UserWarning: 'MetaDict' object uses 'items' internally. 'items' can only be accessed via `obj['items']`.\n print(cfg)\n >> {'items': [1, 2, 3]}\n print(cfg['items'])\n >> [1, 2, 3]\n \n # But the items method is not overwritten!\n print(cfg.items)\n >> <bound method Mapping.items of {'items': [1, 2, 3]}>\n print(list(cfg.items()))\n >> [('items', [1, 2, 3])]\n ```\n- References are preserved\n ```python\n params = [1, 2, 3] \n cfg = MetaDict({'params': params})\n print(cfg.params is params)\n >> True\n \n model_dict = {'params': params}\n cfg = MetaDict(model=model_dict)\n print(cfg.model.params is params)\n >> True\n \n # Note: dicts are recursively converted to MetaDicts, thus...\n print(cfg.model is model_dict)\n >> False\n print(cfg.model == model_dict)\n >> True\n ```\n\n## Documentation\n\nCheck the [Test Cases](https://github.com/LarsHill/metadict/blob/main/tests/test_metadict.py) for a complete overview of all **MetaDict** features.\n\n\n## Competitors\n- [Addict](https://github.com/mewwts/addict)\n - No key autocompletion in IDE\n - Nested key assignment cannot be turned off\n - Newly assigned `dict` objects are not converted to support attribute-style key access\n - Shadows inbuilt type `Dict`\n- [Prodict](https://github.com/ramazanpolat/prodict)\n - No key autocompletion in IDE without defining a static schema (similar to `dataclass`)\n - No recursive conversion of `dict` objects when embedded in `list` or other inbuilt iterables\n- [AttrDict](https://github.com/bcj/AttrDict)\n - No key autocompletion in IDE\n - Converts `list` objects to `tuple` behind the scenes\n- [Munch](https://github.com/Infinidat/munch)\n - Inbuilt methods like `items()`, `update()`, etc. can be overwritten with `obj.items = [1, 2, 3]` \n - No recursive conversion of `dict` objects when embedded in `list` or other inbuilt iterables\n- [EasyDict](https://github.com/makinacorpus/easydict)\n - Only strings are valid keys, but `dict` accepts all hashable objects as keys\n - Inbuilt methods like `items()`, `update()`, etc. can be overwritten with `obj.items = [1, 2, 3]`\n - Inbuilt methods don't behave as expected: `obj.pop('unknown_key', None)` raises an `AttributeError`\n\n\n## Citation\n\n```\n@article{metadict,\n title = {MetaDict - Enabling dot notation and IDE autocompletion},\n author = {Hillebrand, Lars},\n year = {2022},\n publisher = {GitHub},\n journal = {GitHub repository},\n howpublished = {\\url{https://github.com/LarsHill/metadict}},\n}\n```\n",
"bugtrack_url": null,
"license": "Apache-2.0",
"summary": "MetaDict is a powerful dict subclass enabling (nested) attribute-style item access/assignment and IDE autocompletion support.",
"version": "0.1.5",
"project_urls": {
"Download": "https://github.com/LarsHill/metadict/",
"Homepage": "https://github.com/LarsHill/metadict/"
},
"split_keywords": [
"dict",
" attribute-style syntax",
" nesting",
" auto-completion"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "5b62526f926e3cfbc0cd136480b5902f920502aa41a0c914db3e20ea2804d64c",
"md5": "a3ea685bda910bae69d1dfae4dc40fe9",
"sha256": "e374cf55cd7e182135f35c50f9c4e83b8f0d70e74b626f6c4499a01267ad0f80"
},
"downloads": -1,
"filename": "metadict-0.1.5-py3-none-any.whl",
"has_sig": false,
"md5_digest": "a3ea685bda910bae69d1dfae4dc40fe9",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 11192,
"upload_time": "2025-07-19T11:23:37",
"upload_time_iso_8601": "2025-07-19T11:23:37.744881Z",
"url": "https://files.pythonhosted.org/packages/5b/62/526f926e3cfbc0cd136480b5902f920502aa41a0c914db3e20ea2804d64c/metadict-0.1.5-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "d1e1b1831cf30286259e8250c9ec2814cc27c1110a91437723f85fb5b74138ab",
"md5": "a555cf9d90a1060a31493316b4ee8fa5",
"sha256": "4ebd972a4bd1d22e72a158bfc6ff8d497deab7ab220fa27ad128fcee4f54f300"
},
"downloads": -1,
"filename": "metadict-0.1.5.tar.gz",
"has_sig": false,
"md5_digest": "a555cf9d90a1060a31493316b4ee8fa5",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 10880,
"upload_time": "2025-07-19T11:23:38",
"upload_time_iso_8601": "2025-07-19T11:23:38.913845Z",
"url": "https://files.pythonhosted.org/packages/d1/e1/b1831cf30286259e8250c9ec2814cc27c1110a91437723f85fb5b74138ab/metadict-0.1.5.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-19 11:23:38",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "LarsHill",
"github_project": "metadict",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"circle": true,
"lcname": "metadict"
}