attrdict


Nameattrdict JSON
Version 2.0.1 PyPI version JSON
download
home_pagehttps://github.com/bcj/AttrDict
SummaryA dict with attribute-style access
upload_time2019-02-01 22:33:58
maintainer
docs_urlNone
authorBrendan Curran-Johnson
requires_python
licenseMIT License
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI
coveralls test coverage No coveralls.
            ========
AttrDict
========
.. image:: https://travis-ci.org/bcj/AttrDict.svg?branch=master
  :target: https://travis-ci.org/bcj/AttrDict?branch=master
.. image:: https://coveralls.io/repos/bcj/AttrDict/badge.png?branch=master
  :target: https://coveralls.io/r/bcj/AttrDict?branch=master

AttrDict is an MIT-licensed library that provides mapping objects that allow
their elements to be accessed both as keys and as attributes::

    > from attrdict import AttrDict
    > a = AttrDict({'foo': 'bar'})
    > a.foo
    'bar'
    > a['foo']
    'bar'

Attribute access makes it easy to create convenient, hierarchical settings
objects::

    with open('settings.yaml') as fileobj:
        settings = AttrDict(yaml.safe_load(fileobj))

    cursor = connect(**settings.db.credentials).cursor()

    cursor.execute("SELECT column FROM table;")

Installation
============
AttrDict is in PyPI, so it can be installed directly using::

    $ pip install attrdict

Or from Github::

    $ git clone https://github.com/bcj/AttrDict
    $ cd AttrDict
    $ python setup.py install

Basic Usage
===========
AttrDict comes with three different classes, `AttrMap`, `AttrDict`, and
`AttrDefault`. They are all fairly similar, as they all are MutableMappings (
read: dictionaries) that allow creating, accessing, and deleting key-value
pairs as attributes.

Valid Names
-----------
Any key can be used as an attribute as long as:

#. The key represents a valid attribute (i.e., it is a string comprised only of
   alphanumeric characters and underscores that doesn't start with a number)
#. The key represents a public attribute (i.e., it doesn't start with an
   underscore). This is done (in part) so that implementation changes between
   minor and micro versions don't force major version changes.
#. The key does not shadow a class attribute (e.g., get).

Attributes vs. Keys
-------------------
There is a minor difference between accessing a value as an attribute vs.
accessing it as a key, is that when a dict is accessed as an attribute, it will
automatically be converted to an Attr object. This allows you to recursively
access keys::

    > attr = AttrDict({'foo': {'bar': 'baz'}})
    > attr.foo.bar
    'baz'

Relatedly, by default, sequence types that aren't `bytes`, `str`, or `unicode`
(e.g., lists, tuples) will automatically be converted to tuples, with any
mappings converted to Attrs::

    > attr = AttrDict({'foo': [{'bar': 'baz'}, {'bar': 'qux'}]})
    > for sub_attr in attr.foo:
    >     print(sub_attr.foo)
    'baz'
    'qux'

To get this recursive functionality for keys that cannot be used as attributes,
you can replicate the behavior by calling the Attr object::

    > attr = AttrDict({1: {'two': 3}})
    > attr(1).two
    3

Classes
-------
AttrDict comes with three different objects, `AttrMap`, `AttrDict`, and
`AttrDefault`.

AttrMap
^^^^^^^
The most basic implementation. Use this if you want to limit the number of
invalid keys, or otherwise cannot use `AttrDict`

AttrDict
^^^^^^^^
An Attr object that subclasses `dict`. You should be able to use this
absolutely anywhere you can use a `dict`. While this is probably the class you
want to use, there are a few caveats that follow from this being a `dict` under
the hood.

The `copy` method (which returns a shallow copy of the mapping) returns a
`dict` instead of an `AttrDict`.

Recursive attribute access results in a shallow copy, so recursive assignment
will fail (as you will be writing to a copy of that dictionary)::

    > attr = AttrDict('foo': {})
    > attr.foo.bar = 'baz'
    > attr.foo
    {}

Assignment as keys will still work::

    > attr = AttrDict('foo': {})
    > attr['foo']['bar'] = 'baz'
    > attr.foo
    {'bar': 'baz'}

If either of these caveats are deal-breakers, or you don't need your object to
be a `dict`, consider using `AttrMap` instead.

AttrDefault
^^^^^^^^^^^
At Attr object that behaves like a `defaultdict`. This allows on-the-fly,
automatic key creation::

    > attr = AttrDefault(int, {})
    > attr.foo += 1
    > attr.foo
    1

AttrDefault also has a `pass_key` option that passes the supplied key to the
`default_factory`::

    > attr = AttrDefault(sorted, {}, pass_key=True)
    > attr.banana
    ['a', 'a', 'a', 'b', 'n', 'n']

Merging
-------
All three Attr classes can be merged with eachother or other Mappings using the
``+`` operator. For conflicting keys, the right dict's value will be
preferred, but in the case of two dictionary values, they will be
recursively merged::

    > a = {'foo': 'bar', 'alpha': {'beta': 'a', 'a': 'a'}}
    > b = {'lorem': 'ipsum', 'alpha': {'bravo': 'b', 'a': 'b'}}
    > AttrDict(a) + b
    {'foo': 'bar', 'lorem': 'ipsum', 'alpha': {'beta': 'a', 'bravo': 'b', 'a': 'b'}}

NOTE: AttrDict's add is not commutative, ``a + b != b + a``::

    > a = {'foo': 'bar', 'alpha': {'beta': 'b', 'a': 0}}
    > b = {'lorem': 'ipsum', 'alpha': {'bravo': 'b', 'a': 1}}
    > b + AttrDict(a)
    {'foo': 'bar', 'lorem': 'ipsum', 'alpha': {'beta': 'a', 'bravo': 'b', 'a': }}

Sequences
---------
By default, items in non-string Sequences (e.g. lists, tuples) will be
converted to AttrDicts::

    > adict = AttrDict({'list': [{'value': 1}, {'value': 2}]})
    > for element in adict.list:
    >     element.value
    1
    2

This will not occur if you access the AttrDict as a dictionary::

    > adict = AttrDict({'list': [{'value': 1}, {'value': 2}]})
    > for element in adict['list']:
    >     isinstance(element, AttrDict)
    False
    False

To disable this behavior globally, pass the attribute ``recursive=False`` to
the constructor::

    > adict = AttrDict({'list': [{'value': 1}, {'value': 2}]}, recursive=False)
    > for element in adict.list:
    >     isinstance(element, AttrDict)
    False
    False

When merging an AttrDict with another mapping, this behavior will be disabled
if at least one of the merged items is an AttrDict that has set ``recursive``
to ``False``.

License
=======
AttrDict is released under a MIT license.



            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/bcj/AttrDict",
    "name": "attrdict",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "",
    "author": "Brendan Curran-Johnson",
    "author_email": "brendan@bcjbcj.ca",
    "download_url": "https://files.pythonhosted.org/packages/3f/72/614aae677d28e81a5bf830fadcf580803876ef76e0306902d3ca5790cd9a/attrdict-2.0.1.tar.gz",
    "platform": "",
    "description": "========\nAttrDict\n========\n.. image:: https://travis-ci.org/bcj/AttrDict.svg?branch=master\n  :target: https://travis-ci.org/bcj/AttrDict?branch=master\n.. image:: https://coveralls.io/repos/bcj/AttrDict/badge.png?branch=master\n  :target: https://coveralls.io/r/bcj/AttrDict?branch=master\n\nAttrDict is an MIT-licensed library that provides mapping objects that allow\ntheir elements to be accessed both as keys and as attributes::\n\n    > from attrdict import AttrDict\n    > a = AttrDict({'foo': 'bar'})\n    > a.foo\n    'bar'\n    > a['foo']\n    'bar'\n\nAttribute access makes it easy to create convenient, hierarchical settings\nobjects::\n\n    with open('settings.yaml') as fileobj:\n        settings = AttrDict(yaml.safe_load(fileobj))\n\n    cursor = connect(**settings.db.credentials).cursor()\n\n    cursor.execute(\"SELECT column FROM table;\")\n\nInstallation\n============\nAttrDict is in PyPI, so it can be installed directly using::\n\n    $ pip install attrdict\n\nOr from Github::\n\n    $ git clone https://github.com/bcj/AttrDict\n    $ cd AttrDict\n    $ python setup.py install\n\nBasic Usage\n===========\nAttrDict comes with three different classes, `AttrMap`, `AttrDict`, and\n`AttrDefault`. They are all fairly similar, as they all are MutableMappings (\nread: dictionaries) that allow creating, accessing, and deleting key-value\npairs as attributes.\n\nValid Names\n-----------\nAny key can be used as an attribute as long as:\n\n#. The key represents a valid attribute (i.e., it is a string comprised only of\n   alphanumeric characters and underscores that doesn't start with a number)\n#. The key represents a public attribute (i.e., it doesn't start with an\n   underscore). This is done (in part) so that implementation changes between\n   minor and micro versions don't force major version changes.\n#. The key does not shadow a class attribute (e.g., get).\n\nAttributes vs. Keys\n-------------------\nThere is a minor difference between accessing a value as an attribute vs.\naccessing it as a key, is that when a dict is accessed as an attribute, it will\nautomatically be converted to an Attr object. This allows you to recursively\naccess keys::\n\n    > attr = AttrDict({'foo': {'bar': 'baz'}})\n    > attr.foo.bar\n    'baz'\n\nRelatedly, by default, sequence types that aren't `bytes`, `str`, or `unicode`\n(e.g., lists, tuples) will automatically be converted to tuples, with any\nmappings converted to Attrs::\n\n    > attr = AttrDict({'foo': [{'bar': 'baz'}, {'bar': 'qux'}]})\n    > for sub_attr in attr.foo:\n    >     print(sub_attr.foo)\n    'baz'\n    'qux'\n\nTo get this recursive functionality for keys that cannot be used as attributes,\nyou can replicate the behavior by calling the Attr object::\n\n    > attr = AttrDict({1: {'two': 3}})\n    > attr(1).two\n    3\n\nClasses\n-------\nAttrDict comes with three different objects, `AttrMap`, `AttrDict`, and\n`AttrDefault`.\n\nAttrMap\n^^^^^^^\nThe most basic implementation. Use this if you want to limit the number of\ninvalid keys, or otherwise cannot use `AttrDict`\n\nAttrDict\n^^^^^^^^\nAn Attr object that subclasses `dict`. You should be able to use this\nabsolutely anywhere you can use a `dict`. While this is probably the class you\nwant to use, there are a few caveats that follow from this being a `dict` under\nthe hood.\n\nThe `copy` method (which returns a shallow copy of the mapping) returns a\n`dict` instead of an `AttrDict`.\n\nRecursive attribute access results in a shallow copy, so recursive assignment\nwill fail (as you will be writing to a copy of that dictionary)::\n\n    > attr = AttrDict('foo': {})\n    > attr.foo.bar = 'baz'\n    > attr.foo\n    {}\n\nAssignment as keys will still work::\n\n    > attr = AttrDict('foo': {})\n    > attr['foo']['bar'] = 'baz'\n    > attr.foo\n    {'bar': 'baz'}\n\nIf either of these caveats are deal-breakers, or you don't need your object to\nbe a `dict`, consider using `AttrMap` instead.\n\nAttrDefault\n^^^^^^^^^^^\nAt Attr object that behaves like a `defaultdict`. This allows on-the-fly,\nautomatic key creation::\n\n    > attr = AttrDefault(int, {})\n    > attr.foo += 1\n    > attr.foo\n    1\n\nAttrDefault also has a `pass_key` option that passes the supplied key to the\n`default_factory`::\n\n    > attr = AttrDefault(sorted, {}, pass_key=True)\n    > attr.banana\n    ['a', 'a', 'a', 'b', 'n', 'n']\n\nMerging\n-------\nAll three Attr classes can be merged with eachother or other Mappings using the\n``+`` operator. For conflicting keys, the right dict's value will be\npreferred, but in the case of two dictionary values, they will be\nrecursively merged::\n\n    > a = {'foo': 'bar', 'alpha': {'beta': 'a', 'a': 'a'}}\n    > b = {'lorem': 'ipsum', 'alpha': {'bravo': 'b', 'a': 'b'}}\n    > AttrDict(a) + b\n    {'foo': 'bar', 'lorem': 'ipsum', 'alpha': {'beta': 'a', 'bravo': 'b', 'a': 'b'}}\n\nNOTE: AttrDict's add is not commutative, ``a + b != b + a``::\n\n    > a = {'foo': 'bar', 'alpha': {'beta': 'b', 'a': 0}}\n    > b = {'lorem': 'ipsum', 'alpha': {'bravo': 'b', 'a': 1}}\n    > b + AttrDict(a)\n    {'foo': 'bar', 'lorem': 'ipsum', 'alpha': {'beta': 'a', 'bravo': 'b', 'a': }}\n\nSequences\n---------\nBy default, items in non-string Sequences (e.g. lists, tuples) will be\nconverted to AttrDicts::\n\n    > adict = AttrDict({'list': [{'value': 1}, {'value': 2}]})\n    > for element in adict.list:\n    >     element.value\n    1\n    2\n\nThis will not occur if you access the AttrDict as a dictionary::\n\n    > adict = AttrDict({'list': [{'value': 1}, {'value': 2}]})\n    > for element in adict['list']:\n    >     isinstance(element, AttrDict)\n    False\n    False\n\nTo disable this behavior globally, pass the attribute ``recursive=False`` to\nthe constructor::\n\n    > adict = AttrDict({'list': [{'value': 1}, {'value': 2}]}, recursive=False)\n    > for element in adict.list:\n    >     isinstance(element, AttrDict)\n    False\n    False\n\nWhen merging an AttrDict with another mapping, this behavior will be disabled\nif at least one of the merged items is an AttrDict that has set ``recursive``\nto ``False``.\n\nLicense\n=======\nAttrDict is released under a MIT license.\n\n\n",
    "bugtrack_url": null,
    "license": "MIT License",
    "summary": "A dict with attribute-style access",
    "version": "2.0.1",
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "md5": "b10e44e149116ba79ab6c04ec9c892e7",
                "sha256": "9432e3498c74ff7e1b20b3d93b45d766b71cbffa90923496f82c4ae38b92be34"
            },
            "downloads": -1,
            "filename": "attrdict-2.0.1-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "b10e44e149116ba79ab6c04ec9c892e7",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": null,
            "size": 9946,
            "upload_time": "2019-02-01T22:33:56",
            "upload_time_iso_8601": "2019-02-01T22:33:56.508208Z",
            "url": "https://files.pythonhosted.org/packages/ef/97/28fe7e68bc7adfce67d4339756e85e9fcf3c6fd7f0c0781695352b70472c/attrdict-2.0.1-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "md5": "6d541c82592568dc0dfcecf57978b326",
                "sha256": "35c90698b55c683946091177177a9e9c0713a0860f0e049febd72649ccd77b70"
            },
            "downloads": -1,
            "filename": "attrdict-2.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "6d541c82592568dc0dfcecf57978b326",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 9593,
            "upload_time": "2019-02-01T22:33:58",
            "upload_time_iso_8601": "2019-02-01T22:33:58.493807Z",
            "url": "https://files.pythonhosted.org/packages/3f/72/614aae677d28e81a5bf830fadcf580803876ef76e0306902d3ca5790cd9a/attrdict-2.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2019-02-01 22:33:58",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "github_user": "bcj",
    "github_project": "AttrDict",
    "travis_ci": true,
    "coveralls": false,
    "github_actions": false,
    "tox": true,
    "lcname": "attrdict"
}
        
Elapsed time: 0.02046s