Python: Accessing YAML values using "dot notation"
<p>I'm using a YAML configuration file. So this is the code to load my config in Python:</p>

<pre><code>import os
import yaml
with open('./config.yml') as file:
    config = yaml.safe_load(file)
</code></pre>

<p>This code actually creates a dictionary. Now the problem is that in order to access the values I need to use tons of brackets.</p>

<p>YAML:</p>

<pre><code>mysql:
    user:
        pass: secret
</code></pre>

<p>Python:</p>

<pre><code>import os
import yaml
with open('./config.yml') as file:
    config = yaml.safe_load(file)
print(config['mysql']['user']['pass']) # &lt;--
</code></pre>

<p>I'd prefer something like that (dot notation):</p>

<pre><code>config('mysql.user.pass')
</code></pre>

<p>So, my idea is to utilize the PyStache render() interface.</p>

<pre><code>import os
import yaml
with open('./config.yml') as file:
    config = yaml.safe_load(file)

import pystache
def get_config_value( yml_path, config ):
    return pystache.render('{{' + yml_path + '}}', config)

get_config_value('mysql.user.pass', config)
</code></pre>

<p>Would that be a "good" solution? If not, what would be a better alternative?</p>

<p><strong>Additional question [Solved]</strong></p>

<p>I've decided to use Ilja EverilÃ¤'s solution. But now I've got an additional question: How would you create a wrapper Config class around DotConf?</p>

<p>The following code doesn't work but I hope you get the idea what I'm trying to do:</p>

<pre><code>class Config( DotDict ):
    def __init__( self ):
        with open('./config.yml') as file:
            DotDict.__init__(yaml.safe_load(file))

config = Config()
print(config.django.admin.user)
</code></pre>

<p>Error: </p>

<pre><code>AttributeError: 'super' object has no attribute '__getattr__'
</code></pre>

<p><strong>Solution</strong></p>

<p>You just need to pass <code>self</code> to the constructor of the super class.</p>

<pre><code>DotDict.__init__(self, yaml.safe_load(file))
</code></pre>

<p><strong>Even better soltution (Ilja EverilÃ¤)</strong></p>

<pre><code>super().__init__(yaml.safe_load(file))
</code></pre>

<h1>The Simple</h1>

<p>You could use <a href="https://docs.python.org/3/library/functools.html#functools.reduce" rel="nofollow"><code>reduce</code></a> to extract the value from the config:</p>

<pre><code>In [41]: config = {'asdf': {'asdf': {'qwer': 1}}}

In [42]: from functools import reduce
    ...: 
    ...: def get_config_value(key, cfg):
    ...:     return reduce(lambda c, k: c[k], key.split('.'), cfg)
    ...: 

In [43]: get_config_value('asdf.asdf.qwer', config)
Out[43]: 1
</code></pre>

<p>This solution is easy to maintain and has very few new edge cases, if your YAML uses a very limited subset of the language.</p>

<h1>The Correct</h1>

<p>Use a proper YAML parser and tools, such as in <a href="http://stackoverflow.com/a/39485868/2681632">this answer</a>.</p>

<hr>

<h1>The Convoluted</h1>

<p>On a lighter note (not to be taken too seriously), you could create a wrapper that allows using attribute access:</p>

<pre><code>In [47]: class DotConfig:
    ...:     
    ...:     def __init__(self, cfg):
    ...:         self._cfg = cfg
    ...:     def __getattr__(self, k):
    ...:         v = self._cfg[k]
    ...:         if isinstance(v, dict):
    ...:             return DotConfig(v)
    ...:         return v
    ...:     

In [48]: DotConfig(config).asdf.asdf.qwer
Out[48]: 1
</code></pre>

<p>Do note that this fails for keywords, such as "as", "pass", "if" and the like.</p>

<p>Finally, you could get really crazy (read: probably not a good idea) and customize <code>dict</code> to handle dotted string and tuple keys as a special case, with attribute access to items thrown in the mix (with its limitations):</p>

<pre><code>In [58]: class DotDict(dict):
    ...:     
    ...:     # update, __setitem__ etc. omitted, but required if
    ...:     # one tries to set items using dot notation. Essentially
    ...:     # this is a read-only view.
    ...:
    ...:     def __getattr__(self, k):
    ...:         try:
    ...:             v = self[k]
    ...:         except KeyError:
    ...:             return super().__getattr__(k)
    ...:         if isinstance(v, dict):
    ...:             return DotDict(v)
    ...:         return v
    ...:
    ...:     def __getitem__(self, k):
    ...:         if isinstance(k, str) and '.' in k:
    ...:             k = k.split('.')
    ...:         if isinstance(k, (list, tuple)):
    ...:             return reduce(lambda d, kk: d[kk], k, self)
    ...:         return super().__getitem__(k)
    ...:
    ...:     def get(self, k, default=None):
    ...:         if isinstance(k, str) and '.' in k:
    ...:             try:
    ...:                 return self[k]
    ...:             except KeyError:
    ...:                 return default
    ...:         return super().get(k, default=default)
    ...:     

In [59]: dotconf = DotDict(config)

In [60]: dotconf['asdf.asdf.qwer']
Out[60]: 1

In [61]: dotconf['asdf', 'asdf', 'qwer']
Out[61]: 1

In [62]: dotconf.asdf.asdf.qwer
Out[62]: 1

In [63]: dotconf.get('asdf.asdf.qwer')
Out[63]: 1

In [64]: dotconf.get('asdf.asdf.asdf')

In [65]: dotconf.get('asdf.asdf.asdf', 'Nope')
Out[65]: 'Nope'
</code></pre>

python python-3.x yaml