nattrs


Namenattrs JSON
Version 0.2.2 PyPI version JSON
download
home_pagehttps://github.com/ludvigolsen/nattrs
SummaryNested attribute getter/setter that works with dictionaries and objects interchangeably.
upload_time2024-09-07 22:46:24
maintainerNone
docs_urlNone
authorLudvig Renbo Olsen
requires_python<4.0,>=3.8
licenseMIT
keywords attributes dictionaries class recursive
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            
# 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"
}
        
Elapsed time: 1.15108s