# nattrs <a href='https://github.com/LudvigOlsen/nattrs'><img src='https://raw.githubusercontent.com/LudvigOlsen/nattrs/main/nattrs_242x280_259dpi.png' align="right" height="140" /></a>
**Nested attributes** utility functions for python. Allows getting/setting of object attributes and dict members interchangeably.
Useful to populate nested dicts for storing outputs of a loop.
The functions should work with most dict types (i.e. `Mapping` / `MutableMapping`) and classes.
Tip: Attribute names can be found using regular expressions.
> https://pypi.python.org/pypi/nattrs/
# Installation
Install from PyPI:
```shell
pip install nattrs
```
Install from GitHub:
```shell
python -m pip install git+https://github.com/ludvigolsen/nattrs
```
# Main functions
| Class/Function | Description |
| :----------------- | :--------------------------------------------------------------------------------------------------------- |
| `nested_getattr` | Get object attributes/dict members recursively, given by dot-separated names. |
| `nested_setattr` | Set object attribute/dict member by recursive lookup, given by dot-separated names. |
| `nested_mutattr` | Apply function (mutate) to object attribute/dict member by recursive lookup, given by dot-separated names. |
| `nested_updattr` | Update dict / class.__dict__ of attributes recursively, given by dot-separated names. |
| `nested_hasattr` | Check whether recursive object attributes/dict members exist. |
| `nested_delattr` | Delete object attributes/dict members recursively, given by dot-separated names. |
| `populate_product` | Create and populate nested dicts with specified layers and the same leaf value. |
# Examples
Create class `B` with a dict `c` with the member `d`:
```python
class B:
def __init__(self):
self.c = {
"d": 1
}
```
Add to a dict `a`:
```python
a = {"b": B()}
```
## nested_getattr
Get the value of `d`:
```python
nested_getattr(a, "b.c.d")
>> 1
```
Get default value when not finding an attribute:
```python
nested_getattr(a, "b.o.p", default="not found")
>> "not found"
```
## nested_setattr
Set the value of `d`:
```python
nested_setattr(a, "b.c.d", 2)
```
Check new value of `d`:
```python
nested_getattr(a, "b.c.d")
>> 2
```
## nested_mutattr
Mutate `d` with an anonymous function (lambda):
```python
nested_mutattr(a, "b.c.d", lambda x: x * 5)
```
Check new value of `d`:
```python
nested_getattr(a, "b.c.d")
>> 10
```
Note: If your function performs the assignment *in-place*, remember to enable the `is_inplace_fn` argument.
## nested_updattr
Update `d` with a dictionary:
```python
nested_updattr(a, "b.c.d", {"d": 3})
```
Check new value of `d`:
```python
nested_getattr(a, "b.c.d")
>> 3
```
Note: Also work on `class.__dict__` dicts.
## nested_hasattr
Check presence of the member 'd':
```python
nested_hasattr(a, "b.c.d")
>> True
```
Fail to find member 'o':
```python
nested_hasattr(a, "b.o.p")
>> False
```
## nested_delattr
Delete the member 'd':
```python
nested_delattr(a, "b.c.d")
nested_hasattr(a, "b.c.d")
>> False
```
Ignore that it fails to find member 'o':
```python
nested_delattr(a, "b.o.p", allow_missing=True)
```
## populate_product
In this example, we wish to pre-populate nested dicts with empty lists to allow appending within a `for` loop. First, we go through the manual approach of doing this. Second, we show how easy it is to do with `populate_product()`.
Say we have 3 variables that can each hold 2 values. We want to compute *something* for each combination of these values. Let's first define these variables and their options:
```python
animal = ["cat", "dog"]
food = ["strawberry", "cucumber"]
temperature = ["cold", "warm"]
```
Let's generate the product of these options:
```python
import itertools
combinations = list(itertools.product(*[animal, food, temperature]))
combinations
>> [('cat', 'strawberry', 'cold'),
>> ('cat', 'strawberry', 'warm'),
>> ('cat', 'cucumber', 'cold'),
>> ('cat', 'cucumber', 'warm'),
>> ('dog', 'strawberry', 'cold'),
>> ('dog', 'strawberry', 'warm'),
>> ('dog', 'cucumber', 'cold'),
>> ('dog', 'cucumber', 'warm')]
```
Now we can create a nested dict structure with a list in the leaf element:
```python
# Initialize empty dict
nested_dict = {}
for leaf in combinations:
# Join each string with dot-separation:
attr = ".".join(list(leaf))
# Assign empty list to the leafs
# `make_missing` creates dicts for each
# missing attribute/dict member
nattrs.nested_setattr(
obj=nested_dict,
attr=attr,
value=[],
make_missing=True
)
nested_dict
>> {'cat': {'strawberry': {'cold': [], 'warm': []},
>> 'cucumber': {'cold': [], 'warm': []}},
>> 'dog': {'strawberry': {'cold': [], 'warm': []},
>> 'cucumber': {'cold': [], 'warm': []}}}
```
This dict population is actually provided by `populate_product()`. Instead of an empty list, let's set the value to an "edibility" score that could be changed by a later function:
```python
layers = [animal, food, temperature]
populate_product(
layers=layers,
val=False
)
>> {'cat': {'strawberry': {'cold': False, 'warm': False},
>> 'cucumber': {'cold': False, 'warm': False}},
>> 'dog': {'strawberry': {'cold': False, 'warm': False},
>> 'cucumber': {'cold': False, 'warm': False}}}
```
Raw data
{
"_id": null,
"home_page": "https://github.com/ludvigolsen/nattrs",
"name": "nattrs",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.8",
"maintainer_email": null,
"keywords": "attributes, dictionaries, class, recursive",
"author": "Ludvig Renbo Olsen",
"author_email": "mail@ludvigolsen.dk",
"download_url": "https://files.pythonhosted.org/packages/40/49/d18ef6c24e2a0723ae2b116ebb74726e358256450f952eccfe7b433fe658/nattrs-0.2.2.tar.gz",
"platform": null,
"description": "\n# nattrs <a href='https://github.com/LudvigOlsen/nattrs'><img src='https://raw.githubusercontent.com/LudvigOlsen/nattrs/main/nattrs_242x280_259dpi.png' align=\"right\" height=\"140\" /></a>\n\n**Nested attributes** utility functions for python. Allows getting/setting of object attributes and dict members interchangeably.\nUseful to populate nested dicts for storing outputs of a loop.\n\nThe functions should work with most dict types (i.e. `Mapping` / `MutableMapping`) and classes.\n\nTip: Attribute names can be found using regular expressions.\n\n> https://pypi.python.org/pypi/nattrs/ \n\n\n# Installation\n\nInstall from PyPI:\n\n```shell\npip install nattrs\n```\n\nInstall from GitHub:\n\n```shell\npython -m pip install git+https://github.com/ludvigolsen/nattrs\n```\n\n\n# Main functions\n\n| Class/Function | Description |\n| :----------------- | :--------------------------------------------------------------------------------------------------------- |\n| `nested_getattr` | Get object attributes/dict members recursively, given by dot-separated names. |\n| `nested_setattr` | Set object attribute/dict member by recursive lookup, given by dot-separated names. |\n| `nested_mutattr` | Apply function (mutate) to object attribute/dict member by recursive lookup, given by dot-separated names. |\n| `nested_updattr` | Update dict / class.__dict__ of attributes recursively, given by dot-separated names. |\n| `nested_hasattr` | Check whether recursive object attributes/dict members exist. |\n| `nested_delattr` | Delete object attributes/dict members recursively, given by dot-separated names. |\n| `populate_product` | Create and populate nested dicts with specified layers and the same leaf value. |\n\n\n# Examples\n\nCreate class `B` with a dict `c` with the member `d`:\n\n```python\n\nclass B:\n def __init__(self):\n self.c = {\n \"d\": 1\n }\n\n```\n\nAdd to a dict `a`:\n\n```python\n\na = {\"b\": B()}\n\n```\n\n## nested_getattr\n\nGet the value of `d`:\n\n```python\n\nnested_getattr(a, \"b.c.d\")\n>> 1\n\n```\n\nGet default value when not finding an attribute:\n\n```python\n\nnested_getattr(a, \"b.o.p\", default=\"not found\")\n>> \"not found\"\n\n```\n\n## nested_setattr\n\nSet the value of `d`:\n\n```python\n\nnested_setattr(a, \"b.c.d\", 2)\n\n```\n\nCheck new value of `d`:\n\n```python\n\nnested_getattr(a, \"b.c.d\")\n>> 2\n\n```\n\n## nested_mutattr\n\nMutate `d` with an anonymous function (lambda):\n\n```python\n\nnested_mutattr(a, \"b.c.d\", lambda x: x * 5)\n\n```\n\nCheck new value of `d`:\n\n```python\n\nnested_getattr(a, \"b.c.d\")\n>> 10\n\n```\n\nNote: If your function performs the assignment *in-place*, remember to enable the `is_inplace_fn` argument.\n\n## nested_updattr\n\nUpdate `d` with a dictionary:\n\n```python\n\nnested_updattr(a, \"b.c.d\", {\"d\": 3})\n\n```\n\nCheck new value of `d`:\n\n```python\n\nnested_getattr(a, \"b.c.d\")\n>> 3\n\n```\n\nNote: Also work on `class.__dict__` dicts.\n\n\n## nested_hasattr\n\nCheck presence of the member 'd':\n\n```python\n\nnested_hasattr(a, \"b.c.d\")\n>> True\n\n```\n\nFail to find member 'o':\n\n```python\n\nnested_hasattr(a, \"b.o.p\")\n>> False\n\n```\n\n## nested_delattr\n\nDelete the member 'd':\n\n```python\n\nnested_delattr(a, \"b.c.d\")\nnested_hasattr(a, \"b.c.d\")\n>> False\n\n```\n\nIgnore that it fails to find member 'o':\n\n```python\n\nnested_delattr(a, \"b.o.p\", allow_missing=True)\n\n```\n\n## populate_product\n\nIn this example, we wish to pre-populate nested dicts with empty lists to allow appending within a `for` loop. First, we go through the manual approach of doing this. Second, we show how easy it is to do with `populate_product()`. \n\nSay we have 3 variables that can each hold 2 values. We want to compute *something* for each combination of these values. Let's first define these variables and their options:\n\n```python\n\nanimal = [\"cat\", \"dog\"]\nfood = [\"strawberry\", \"cucumber\"]\ntemperature = [\"cold\", \"warm\"]\n\n```\n\nLet's generate the product of these options:\n\n```python\n\nimport itertools\n\ncombinations = list(itertools.product(*[animal, food, temperature]))\ncombinations\n>> [('cat', 'strawberry', 'cold'),\n>> ('cat', 'strawberry', 'warm'),\n>> ('cat', 'cucumber', 'cold'),\n>> ('cat', 'cucumber', 'warm'),\n>> ('dog', 'strawberry', 'cold'),\n>> ('dog', 'strawberry', 'warm'),\n>> ('dog', 'cucumber', 'cold'),\n>> ('dog', 'cucumber', 'warm')]\n\n```\n\nNow we can create a nested dict structure with a list in the leaf element:\n\n```python\n\n# Initialize empty dict\nnested_dict = {}\n\nfor leaf in combinations:\n # Join each string with dot-separation:\n attr = \".\".join(list(leaf))\n\n # Assign empty list to the leafs\n # `make_missing` creates dicts for each \n # missing attribute/dict member\n nattrs.nested_setattr(\n obj=nested_dict,\n attr=attr,\n value=[],\n make_missing=True\n )\n\nnested_dict\n>> {'cat': {'strawberry': {'cold': [], 'warm': []},\n>> 'cucumber': {'cold': [], 'warm': []}},\n>> 'dog': {'strawberry': {'cold': [], 'warm': []},\n>> 'cucumber': {'cold': [], 'warm': []}}}\n\n```\n\nThis dict population is actually provided by `populate_product()`. Instead of an empty list, let's set the value to an \"edibility\" score that could be changed by a later function:\n\n```python\n\nlayers = [animal, food, temperature]\npopulate_product(\n layers=layers,\n val=False\n)\n>> {'cat': {'strawberry': {'cold': False, 'warm': False},\n>> 'cucumber': {'cold': False, 'warm': False}},\n>> 'dog': {'strawberry': {'cold': False, 'warm': False},\n>> 'cucumber': {'cold': False, 'warm': False}}}\n\n```",
"bugtrack_url": null,
"license": "MIT",
"summary": "Nested attribute getter/setter that works with dictionaries and objects interchangeably.",
"version": "0.2.2",
"project_urls": {
"Homepage": "https://github.com/ludvigolsen/nattrs",
"Repository": "https://github.com/ludvigolsen/nattrs",
"issues": "https://github.com/ludvigolsen/nattrs/issues"
},
"split_keywords": [
"attributes",
" dictionaries",
" class",
" recursive"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "4cd6052ff5356a1f1c54f88709c0697253727dcc6cfedc74cf0090d660375f50",
"md5": "06105524b75eb08a0643bf6f0e666ab7",
"sha256": "3fe1294c4a2e1c7e5772fc37908e64940072a93c1d3c722c6f6b95dbeddf23c3"
},
"downloads": -1,
"filename": "nattrs-0.2.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "06105524b75eb08a0643bf6f0e666ab7",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.8",
"size": 20061,
"upload_time": "2024-09-07T22:46:22",
"upload_time_iso_8601": "2024-09-07T22:46:22.872929Z",
"url": "https://files.pythonhosted.org/packages/4c/d6/052ff5356a1f1c54f88709c0697253727dcc6cfedc74cf0090d660375f50/nattrs-0.2.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "4049d18ef6c24e2a0723ae2b116ebb74726e358256450f952eccfe7b433fe658",
"md5": "a0590b967ccd827f996d30b61e23f741",
"sha256": "533686a410dd9facbb4c83b74b0894a947ca9cd279becc436f0ae0080ca0f3f0"
},
"downloads": -1,
"filename": "nattrs-0.2.2.tar.gz",
"has_sig": false,
"md5_digest": "a0590b967ccd827f996d30b61e23f741",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.8",
"size": 14045,
"upload_time": "2024-09-07T22:46:24",
"upload_time_iso_8601": "2024-09-07T22:46:24.415105Z",
"url": "https://files.pythonhosted.org/packages/40/49/d18ef6c24e2a0723ae2b116ebb74726e358256450f952eccfe7b433fe658/nattrs-0.2.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-09-07 22:46:24",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "ludvigolsen",
"github_project": "nattrs",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "nattrs"
}