【问题标题】:How to load a configuration file in Python and be able to access values using dot notation (attributes)?如何在 Python 中加载配置文件并能够使用点表示法(属性)访问值?
【发布时间】:2012-04-04 03:55:16
【问题描述】:

我正在寻找一种解决方案,可以让我将属性用于配置文件数据。

我希望能够做这样的事情:

config = Config('config.ini')
print config.section1.user
print config.section2.password

我知道 ConfigParser 可以让我做类似config['section1']['user'] 的事情,但这太丑陋了,我们不能做得更好吗?

该解决方案必须适用于 Python 2.5 或更高版本。

【问题讨论】:

  • 您希望如何访问ConfigParser 的所有常用属性?其中有很多,名称如optionsreadgetwrite 等。将这些属性的名称空间与配置文件的名称空间混合是一个坏主意。如果您确认无论如何都想这样做并详细说明您想如何解决冲突,我可能会告诉您如何去做——这并不难,只是一个坏主意。
  • @SvenMarnach:您对属性完全正确,我忘记了它们并发布了一个解决方案。所以问题也是可能的名称冲突,这将使某些配置选项无法通过使用属性访问。

标签: python ini configparser


【解决方案1】:

我已经设法以非常简洁的方式获得了您需要的东西。 使用点符号总是更好,我也希望在我的项目中使用它。 假设在您的项目根目录中,您有 Setup 目录,里面有 setup.cfg。您还将项目根路径添加到 env 变量中(为了使此代码可读)

import os
import configparser


class Section:
    def __init__(self):
        pass


class Setup:
    def __init__(self):
        self.setup_dict = self.load_setup()
        self.fill_setup()

    def load_setup(self):
        setup = configparser.RawConfigParser()
        setup.read(os.environ.get('PROJECT_ROOT_PATH') + "/Setup/setup.cfg")
        return {section: item_dict for (section, item_dict) in zip(list(setup), [dict(section) for section in dict(setup).values()])}

    @staticmethod
    def add_section(data):
        section = Section()
        vars(section).update(data)
        return section

    def fill_setup(self):
        for name in self.setup_dict.keys():
            vars(self).update({name.lower(): self.add_section(self.setup_dict[name])})

问题是,对于读取配置文件时的点表示法,您需要一个类,其中包含代表 cfg 文件部分的类(上面的部分类),而这些类又具有 cfg 部分值的键值对实例变量。

然后您可以从文件中导入设置(或您将调用您的类的任何名称)并像这样使用:

from load_setup import Setup

setup = Setup()
value = setup.section.item

【讨论】:

    【解决方案2】:

    我写了一个简单的包configdot:https://github.com/jjnurminen/configdot。它允许通过语法config.section.item(读写)访问配置项。作为奖励,它还允许您直接在 INI 文件中使用(有限数量的)Python 数据类型。

    【讨论】:

      【解决方案3】:

      我也想使用点符号来访问 ConfigParser 从配置文件中读取的属性。 (Available on github)。

      这是我扩展 ConfigParser 的尝试:

      from ConfigParser import ConfigParser as BaseClass
      
      SPACE = " "
      UNDERSCORE = "_"
      
      
      def internal_name(name, needle=SPACE, replacement=UNDERSCORE):
          return name.replace(needle, replacement)
      
      
      def reverse_name_internalization(name):
          return internal_name(name, needle=UNDERSCORE, replacement=SPACE)
      
      
      class DotNotationConfigParser(BaseClass, object):
      
          def __init__(self, coersion_map=None, *args, **kwargs):
              super(DotNotationConfigParser, self).__init__(*args, **kwargs)
      
              self.optionxform = internal_name
              self.section_attr = None
      
          def get_internalized_section(self, section):
              if self.has_section(section):
                  return internal_name(section)
      
          def __set_section_obj(self, internalized_section):
              if self.has_section(internalized_section):
                  section = internalized_section
              else:
                  section = reverse_name_internalization(internalized_section)
      
              if self.get_internalized_section(section):
                  # set an attr to an object instance with section items
                  obj = type('', (), dict(self.items(section)))()
                  setattr(self, internalized_section, obj)
      
          def __getattr__(self, attr):
              try:
                  return super(DotNotationConfigParser, self).__getattribute__(attr)
              except AttributeError:
                  section = attr
                  self.__set_section_obj(section)
                  return super(DotNotationConfigParser, self).__getattribute__(attr)
      
      
      try:
          from cStringIO import StringIO
      except ImportError:
          from StringIO import StringIO
      configuration_file = """
      [section 1]
      foo = the foo value
      bar = the bar value
      [section 2]
      index = 3
      repeat = False
      [section_n]
      string = This is some text.
      ip = 10.0.1.1
      """
      configuration_file = StringIO(configuration_file)
      
      parser = DotNotationConfigParser()
      parser.readfp(configuration_file)
      
      assert parser.section_1.foo == 'the foo value'
      assert parser.section_1.bar == 'the bar value'
      assert type(parser.section_2.index) is not int
      for section_name in ('section_1', 'section_2', 'section_n'):
          section = getattr(parser, section_name)
          options = [option for option in dir(section)
                     if not option.startswith('__')]
          for option in options:
              print section_name, ": ", getattr(section, option)
      
      print "dot notation", parser.section_1.foo
      

      【讨论】:

        【解决方案4】:

        它并不难看,它更好 - 点表示法意味着在另一个自定义类的对象中可能有一些自定义类的对象。更可行的方法是使用字典(使用括号表示法)。

        但如果你坚持,你可能可以这样翻译代码:

        def config2object(config):
            """
            Convert dictionary into instance allowing access to dictionary keys using
            dot notation (attributes).
            """
            class ConfigObject(dict):
                """
                Represents configuration options' group, works like a dict
                """
                def __init__(self, *args, **kwargs):
                    dict.__init__(self, *args, **kwargs)
                def __getattr__(self, name):
                    return self[name]
                def __setattr__(self, name, val):
                    self[name] = val
            if isinstance(config, dict):
                result = ConfigObject()
                for key in config:
                    result[key] = config2object(config[key])
                return result
            else:
                return config
        

        并且测试显示了预期的结果:

        >>> c1 = {
            'conf1': {
                'key1': 'aaa',
                'key2': 12321,
                'key3': False,
                },
            'conf2': 'bbbb',
            }
        >>> c1
        {'conf2': 'bbbb', 'conf1': {'key3': False, 'key2': 12321, 'key1': 'aaa'}}
        >>> c2 = config2object(c1)
        >>> c2.conf1
        {'key3': False, 'key2': 12321, 'key1': 'aaa'}
        >>> c2.conf1.key1
        'aaa'
        >>> c2.conf1.key3
        False
        >>> c2.conf2
        'bbbb'
        

        编辑:Sven Marnach 指出Config('config.ini') 是一些自定义类实例。它不是字典,它有一些自定义方法可能非常有用,但可能会使某些配置选项无法访问(当存在名称冲突时)。因此,首选方法不是使用我提到的解决方案,而是使用括号表示法来访问配置选项。

        【讨论】:

          猜你喜欢
          • 2012-06-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-03-22
          • 2017-01-20
          • 2021-05-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多