Python: Accessing YAML values using “dot notation”

前端 未结 6 1387
走了就别回头了
走了就别回头了 2020-12-01 15:17

I\'m using a YAML configuration file. So this is the code to load my config in Python:

import os
import yaml
with open(\'./config.yml\') as file:
    config          


        
6条回答
  •  隐瞒了意图╮
    2020-12-01 15:28

    The Simple

    You could use reduce to extract the value from the config:

    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
    

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

    The Correct

    Use a proper YAML parser and tools, such as in this answer.


    The Convoluted

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

    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
    

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

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

    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'
    

提交回复
热议问题