Skip to main content
https://travis-ci.org/bcj/AttrDict.svg?branch=master https://coveralls.io/repos/bcj/AttrDict/badge.png?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_rog 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/root-gift/attrdict-rog.git
$ 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:

  1. 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)

  2. 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.

  3. 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.

Metadata

Release files for attrdict-rog 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for attrdict-rog 1.0.0
File Size Uploaded Provenance
attrdict_rog-1.0.0.tar.gz 18.5 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for attrdict-rog 1.0.0
File Interpreter ABI Platform
attrdict_rog-1.0.0-py2.py3-none-any.whl Python 3, Python 2 none any Details

Total release size: 28.7 kB

Release files / attrdict_rog-1.0.0.tar.gz

Download URL attrdict_rog-1.0.0.tar.gz
Size 18.5 kB
Tags Source
SHA-256 checksum
How to use checksums
28a9ab76de3db6dcd48500804fb890a5aa04a3c5823d46e5ac29afcd0f2bd7e2
BLAKE2b-256 checksum
How to use checksums
b2a54ab29a95527adce3ac22c730e9617b687c9835cc97c8ced837fbd014374a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.0 CPython/3.12.4

Release files / attrdict_rog-1.0.0-py2.py3-none-any.whl

Download URL attrdict_rog-1.0.0-py2.py3-none-any.whl
Size 10.2 kB
Tags Python 2 Python 3
SHA-256 checksum
How to use checksums
08ce97ba0576a029ce078a7848b77a77bf2dc65d0481a05952b844b7c8443801
BLAKE2b-256 checksum
How to use checksums
8ff3fd0f0195021528343f2ac1ee9bf4a08cde6fab853f82f3bb378f4270f8c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.0 CPython/3.12.4

Security for attrdict-rog 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page