Package: chrisdict
chrisdict is an MIT-licensed library that provides mapping objects that allow
their elements to be accessed both as keys and as attributes:> from chrisdict 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;")Installationchrisdict is in PyPI, so it can be installed directly using:$ pip install chrisdictOr from Github:$ git clone https://github.com/chrisjihee/chrisdict
$ cd chrisdict
$ python install .Basic Usagechrisdict comes with three different classes,AttrMap,AttrDict, andAttrDefault. They are all fairly similar, as they all are MutableMappings (
read: dictionaries) that allow creating, accessing, and deleting key-value
pairs as attributes.Valid NamesAny 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. KeysThere 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’tbytes,str, orunicode(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
3Classeschrisdict comes with three different objects,AttrMap,AttrDict, andAttrDefault.AttrMapThe most basic implementation. Use this if you want to limit the number of
invalid keys, or otherwise cannot useAttrDictAttrDictAn Attr object that subclassesdict. You should be able to use this
absolutely anywhere you can use adict. While this is probably the class you
want to use, there are a few caveats that follow from this being adictunder
the hood.Thecopymethod (which returns a shallow copy of the mapping) returns adictinstead of anAttrDict.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 adict, consider usingAttrMapinstead.AttrDefaultAt Attr object that behaves like adefaultdict. This allows on-the-fly,
automatic key creation:> attr = AttrDefault(int, {})
> attr.foo += 1
> attr.foo
1AttrDefault also has apass_keyoption that passes the supplied key to thedefault_factory:> attr = AttrDefault(sorted, {}, pass_key=True)
> attr.banana
['a', 'a', 'a', 'b', 'n', 'n']MergingAll 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': }}SequencesBy 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
2This 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
FalseTo disable this behavior globally, pass the attributerecursive=Falseto
the constructor:> adict = AttrDict({'list': [{'value': 1}, {'value': 2}]}, recursive=False)
> for element in adict.list:
>     isinstance(element, AttrDict)
False
FalseWhen merging an AttrDict with another mapping, this behavior will be disabled
if at least one of the merged items is an AttrDict that has setrecursivetoFalse.Licensechrisdict is released under a MIT license.