【问题标题】:Lists in ConfigParserConfigParser 中的列表
【发布时间】:2010-09-25 01:00:52
【问题描述】:

典型的 ConfigParser 生成的文件如下所示:

[Section]
bar=foo
[Section 2]
bar2= baz

现在,有没有办法索引列表,例如:

[Section 3]
barList={
    item1,
    item2
}

相关问题:Python’s ConfigParser unique keys per section

【问题讨论】:

  • 去掉花括号和逗号,然后使用config['Section 3']['barList'].split()

标签: python configparser


【解决方案1】:

配置解析器仅支持原始类型进行序列化。对于这种要求,我会使用 JSON 或 YAML。

【讨论】:

  • 感谢 utku 的澄清。唯一的问题是我目前无法使用外部软件包。我想我会写一个简单的类来处理这个。我最终会分享它。
  • 你运行的是什么版本的 Python? JSON 模块包含在 2.6 中。
【解决方案2】:

没有什么能阻止您将列表打包成一个分隔字符串,然后在您从配置中获取字符串后将其解包。如果您这样做,您的配置部分将如下所示:

[Section 3]
barList=item1,item2

它并不漂亮,但它对大多数简单列表都很有用。

【讨论】:

  • 如果你有复杂的列表,你可以参考这个问题:stackoverflow.com/questions/330900/… :-)
  • @wim 看我的回答,可以使用 \n 作为分隔符
  • @wim 如果分隔符可以是合法字符,您需要实现一种方法来转义分隔符。 (以及一种逃避您用于逃避的任何字符的方法。)
  • 如果列表只有一个元素怎么办?
  • 听起来不错的拉取请求
【解决方案3】:

我过去也遇到过同样的问题。如果您需要更复杂的列表,请考虑通过从 ConfigParser 继承来创建自己的解析器。然后你会用那个覆盖get方法:

    def get(self, section, option):
    """ Get a parameter
    if the returning value is a list, convert string value to a python list"""
    value = SafeConfigParser.get(self, section, option)
    if (value[0] == "[") and (value[-1] == "]"):
        return eval(value)
    else:
        return value

使用此解决方案,您还可以在配置文件中定义字典。

但要小心!这并不安全:这意味着任何人都可以通过您的配置文件运行代码。如果您的项目中安全性不是问题,我会考虑直接使用 python 类作为配置文件。以下内容比 ConfigParser 文件更强大、更易于使用:

class Section
    bar = foo
class Section2
    bar2 = baz
class Section3
    barList=[ item1, item2 ]

【讨论】:

  • 我正在考虑这样做,但是:为什么不设置像 barList=item1,item2 这样的配置值,然后调用 if value.find(',') > 0: return value.split(','),或者更好的是,让应用程序将所有配置选项解析为列表,只是.split(',')一切都盲目?
【解决方案4】:

参加这个聚会迟到了,但我最近在配置文件中的一个专门部分实现了这一点:

[paths]
path1           = /some/path/
path2           = /another/path/
...

并使用config.items( "paths" ) 获取路径项的可迭代列表,如下所示:

path_items = config.items( "paths" )
for key, path in path_items:
    #do something with path

希望这有助于其他人在谷歌上搜索这个问题;)

【讨论】:

  • 我喜欢这个解决方案,因为您可以; comment 从列表中取出某些项目,而无需重写整个列表。
  • +1,但如果您这样做,请注意同时使用 key,因为 ConfigParser 会将所有此类键转换为小写
  • @AlexDean 您可以通过设置 optionxform = str 来设置 ConfigParser 以保留 camelCase。例子:config = ConfigParser.SafeConfigParser()config.optionxform = str 那么这个案子就不管了
  • @Henry Cooke 当一个键被多次列出时你测试过吗?
  • @DevPlayer 使用多键时,您只能获得最后一个值。 (为了其他读者的利益,回复2岁的评论)
【解决方案5】:

也有点晚了,但可能对某些人有帮助。 我正在使用 ConfigParser 和 JSON 的组合:

[Foo]
fibs: [1,1,2,3,5,8,13]

只需阅读:

>>> json.loads(config.get("Foo","fibs"))
[1, 1, 2, 3, 5, 8, 13]

如果您的列表很长,您甚至可以换行(感谢@peter-smit):

[Bar]
files_to_check = [
     "/path/to/file1",
     "/path/to/file2",
     "/path/to/another file with space in the name"
     ]

当然我可以只使用 JSON,但我发现配置文件更具可读性,并且 [DEFAULT] 部分非常方便。

【讨论】:

  • 这太棒了,因为它会自动“转换”值,如果您事先不知道类型,这会很有用。
  • 我喜欢这个想法,但我只能让它与数字列表一起使用。引号没有帮助。诡异的。继续前进。
  • 你必须有 ["a", "b", "c"] 字符串才能工作。对我来说,这会点击数字,但由于 cfg 文件大多是可编辑的 - 每次添加“”都很痛苦。我宁愿用逗号,然后拆分它。
  • 如何处理原始字符串,例如key5 : [r"abc $x_i$", r"def $y_j$"] ?他们提出错误json.decoder.JSONDecodeError: Expecting value: line 1 column 2 (char 1)
  • Python 3.6 引发错误:json.decoder.JSONDecodeError:预期值:第 1 行第 2 列(字符 1)
【解决方案6】:

很多人不知道的一件事是允许多行配置值。例如:

;test.ini
[hello]
barlist = 
    item1
    item2

config.get('hello','barlist') 的值现在将是:

"\nitem1\nitem2"

您可以使用 splitlines 方法轻松拆分(不要忘记过滤空项目)。

如果我们看一下像 Pyramid 这样的大型框架,他们正在使用这种技术:

def aslist_cronly(value):
    if isinstance(value, string_types):
        value = filter(None, [x.strip() for x in value.splitlines()])
    return list(value)

def aslist(value, flatten=True):
    """ Return a list of strings, separating the input based on newlines
    and, if flatten=True (the default), also split on spaces within
    each line."""
    values = aslist_cronly(value)
    if not flatten:
        return values
    result = []
    for value in values:
        subvalues = value.split()
        result.extend(subvalues)
    return result

Source

我自己,如果这对你来说很常见,我可能会扩展 ConfigParser:

class MyConfigParser(ConfigParser):
    def getlist(self,section,option):
        value = self.get(section,option)
        return list(filter(None, (x.strip() for x in value.splitlines())))

    def getlistint(self,section,option):
        return [int(x) for x in self.getlist(section,option)]

请注意,使用此技术时需要注意一些事项

  1. 作为项目的新行应以空格开头(例如空格或制表符)
  2. 以下所有以空格开头的行都被视为前一项的一部分。此外,如果它有一个 = 符号或者它以 ; 开头跟在空格后面。

【讨论】:

  • 你为什么用.splitlines()而不是.split()?使用每个的默认行为, split 显然是优越的(过滤掉空行)。除非我错过了什么……
  • .split() 在所有空白处中断(除非给出特定字符),.splitlines() 在所有换行符处中断。
  • 啊,好点。我没有想到这一点,因为我的价值观都没有空格。
【解决方案7】:
import ConfigParser
import os

class Parser(object):
    """attributes may need additional manipulation"""
    def __init__(self, section):
        """section to retun all options on, formatted as an object
        transforms all comma-delimited options to lists
        comma-delimited lists with colons are transformed to dicts
        dicts will have values expressed as lists, no matter the length
        """
        c = ConfigParser.RawConfigParser()
        c.read(os.path.join(os.path.dirname(__file__), 'config.cfg'))

        self.section_name = section

        self.__dict__.update({k:v for k, v in c.items(section)})

        #transform all ',' into lists, all ':' into dicts
        for key, value in self.__dict__.items():
            if value.find(':') > 0:
                #dict
                vals = value.split(',')
                dicts = [{k:v} for k, v in [d.split(':') for d in vals]]
                merged = {}
                for d in dicts:
                    for k, v in d.items():
                        merged.setdefault(k, []).append(v)
                self.__dict__[key] = merged
            elif value.find(',') > 0:
                #list
                self.__dict__[key] = value.split(',')

所以现在我的config.cfg 文件看起来像这样:

[server]
credentials=username:admin,password:$3<r3t
loggingdirs=/tmp/logs,~/logs,/var/lib/www/logs
timeoutwait=15

可以为我的小项目解析成足够细粒度的对象。

>>> import config
>>> my_server = config.Parser('server')
>>> my_server.credentials
{'username': ['admin'], 'password', ['$3<r3t']}
>>> my_server.loggingdirs:
['/tmp/logs', '~/logs', '/var/lib/www/logs']
>>> my_server.timeoutwait
'15'

这是为了非常快速地解析简单的配置,如果不转换从Parser 返回的对象,或者重新执行解析器完成的解析工作,您将失去所有获取整数、布尔值和其他类型输出的能力在别处上课。

【讨论】:

    【解决方案8】:

    如果你想字面意思传入一个列表,那么你可以使用:

    ast.literal_eval()
    

    例如配置:

    [section]
    option=["item1","item2","item3"]
    

    代码是:

    import ConfigParser
    import ast
    
    my_list = ast.literal_eval(config.get("section", "option"))
    print(type(my_list))
    print(my_list)
    

    输出:

    <type'list'>
    ["item1","item2","item3"]
    

    【讨论】:

    • 在这种情况下,与使用(可以说更流行的)json.loads() 相比,使用ast.literal_eval() 有什么优势?我认为后者提供了更多的安全性,不是吗?
    • 我很想看到这方面的例子,如果您觉得有帮助,请随时在此线程中添加答案,尽管您的评论本身就是一个很好的问题。我给出的答案简化了来自 ConfigParser 的列表的使用,因此在应用程序内部消除了使用正则表达式的复杂性。没有上下文,我无法评论它的“安全”价值。
    • 我会小心使用literal_eval,它期望在=或之后的python字符串:因此你不能再使用例如path1 = /some/path/ 但 path1 = '/some/path/'
    【解决方案9】:

    我来到这里是为了吃掉这个……

    [global]
    spys = richard.sorge@cccp.gov, mata.hari@deutschland.gov
    

    答案是用逗号分割,去掉空格:

    SPYS = [e.strip() for e in parser.get('global', 'spys').split(',')]
    

    获取列表结果:

    ['richard.sorge@cccp.gov', 'mata.hari@deutschland.gov']
    

    它可能无法准确回答 OP 的问题,但可能是某些人正在寻找的简单答案。

    【讨论】:

    • 我以为迪克在sorger@espionage.su!难怪我的邮件一直弹跳! >_
    • 4 年后读到这篇评论,对着复活节彩蛋咯咯笑
    【解决方案10】:

    这是我用于列表的:

    配置文件内容:

    [sect]
    alist = a
            b
            c
    

    代码:

    l = config.get('sect', 'alist').split('\n')
    

    它适用于字符串

    如果是数字

    配置内容:

    nlist = 1
            2
            3
    

    代码:

    nl = config.get('sect', 'alist').split('\n')
    l = [int(nl) for x in nl]
    

    谢谢。

    【讨论】:

    • 这是我真正想要的,谢谢@LittleEaster
    • 最后一行不应该是l = [int(x) for x in nl]吗?否则不使用x 并返回错误:TypeError: int() argument must be a string, a bytes-like object or a number, not 'list'
    【解决方案11】:

    json.loads & ast.literal_eval 似乎工作但配置中的简单列表将每个字符视为字节,因此返回偶数方括号....

    意思是如果配置有fieldvalue = [1,2,3,4,5]

    然后config.read(*.cfg) config['fieldValue'][0] 返回 [ 代替 1

    【讨论】:

      【解决方案12】:

      在这些答案中没有提到converters kwarg for ConfigParser() 是相当令人失望的。

      根据文档,您可以将字典传递给ConfigParser,这将为解析器和部分代理添加get 方法。所以对于一个列表:

      example.ini

      [Germ]
      germs: a,list,of,names, and,1,2, 3,numbers
      

      解析器示例:

      cp = ConfigParser(converters={'list': lambda x: [i.strip() for i in x.split(',')]})
      cp.read('example.ini')
      cp.getlist('Germ', 'germs')
      ['a', 'list', 'of', 'names', 'and', '1', '2', '3', 'numbers']
      cp['Germ'].getlist('germs')
      ['a', 'list', 'of', 'names', 'and', '1', '2', '3', 'numbers']
      

      这是我个人最喜欢的,因为不需要子类化,而且我不必依赖最终用户来完美地编写 JSON 或可由ast.literal_eval 解释的列表。

      【讨论】:

      • 最佳答案在这里。它使用ConfigParser 已经提供的内容。没有额外的包、方法、类……
      【解决方案13】:

      我在我的项目中完成了类似的任务,其中包含没有值的键的部分:

      import configparser
      
      # allow_no_value param says that no value keys are ok
      config = configparser.ConfigParser(allow_no_value=True)
      
      # overwrite optionxform method for overriding default behaviour (I didn't want lowercased keys)
      config.optionxform = lambda optionstr: optionstr
      
      config.read('./app.config')
      
      features = list(config['FEATURES'].keys())
      
      print(features)
      

      输出:

      ['BIOtag', 'TextPosition', 'IsNoun', 'IsNomn']
      

      app.config:

      [FEATURES]
      BIOtag
      TextPosition
      IsNoun
      IsNomn
      

      【讨论】:

        【解决方案14】:

        正如 Peter Smit (https://stackoverflow.com/a/11866695/7424596) 所述 您可能想要扩展 ConfigParser,此外,可以使用 Interpolator 自动转换为列表和从列表中转换。

        作为参考,您可以在底部找到自动转换配置的代码,例如:

        [DEFAULT]
        keys = [
            Overall cost structure, Capacity, RAW MATERIALS,
            BY-PRODUCT CREDITS, UTILITIES, PLANT GATE COST,
            PROCESS DESCRIPTION, AT 50% CAPACITY, PRODUCTION COSTS,
            INVESTMENT, US$ MILLION, PRODUCTION COSTS, US ¢/LB,
            VARIABLE COSTS, PRODUCTION COSTS, MAINTENANCE MATERIALS
          ]
        

        因此,如果您请求密钥,您将获得:

        <class 'list'>: ['Overall cost structure', 'Capacity', 'RAW MATERIALS', 'BY-PRODUCT CREDITS', 'UTILITIES', 'PLANT GATE COST', 'PROCESS DESCRIPTION', 'AT 50% CAPACITY', 'PRODUCTION COSTS', 'INVESTMENT', 'US$ MILLION', 'PRODUCTION COSTS', 'US ¢/LB', 'VARIABLE COSTS', 'PRODUCTION COSTS', 'MAINTENANCE MATERIALS']
        

        代码:

        class AdvancedInterpolator(Interpolation):
            def before_get(self, parser, section, option, value, defaults):
                is_list = re.search(parser.LIST_MATCHER, value)
                if is_list:
                    return parser.getlist(section, option, raw=True)
                return value
        
        
        class AdvancedConfigParser(ConfigParser):
        
            _DEFAULT_INTERPOLATION = AdvancedInterpolator()
        
            LIST_SPLITTER = '\s*,\s*'
            LIST_MATCHER = '^\[([\s\S]*)\]$'
        
            def _to_list(self, str):
                is_list = re.search(self.LIST_MATCHER, str)
                if is_list:
                    return re.split(self.LIST_SPLITTER, is_list.group(1))
                else:
                    return re.split(self.LIST_SPLITTER, str)
        
        
            def getlist(self, section, option, conv=lambda x:x.strip(), *, raw=False, vars=None,
                          fallback=_UNSET, **kwargs):
                return self._get_conv(
                        section, option,
                        lambda value: [conv(x) for x in self._to_list(value)],
                        raw=raw,
                        vars=vars,
                        fallback=fallback,
                        **kwargs
                )
        
            def getlistint(self, section, option, *, raw=False, vars=None,
                    fallback=_UNSET, **kwargs):
                return self.getlist(section, option, int, raw=raw, vars=vars,
                        fallback=fallback, **kwargs)
        
            def getlistfloat(self, section, option, *, raw=False, vars=None,
                    fallback=_UNSET, **kwargs):
                return self.getlist(section, option, float, raw=raw, vars=vars,
                        fallback=fallback, **kwargs)
        
            def getlistboolean(self, section, option, *, raw=False, vars=None,
                    fallback=_UNSET, **kwargs):
                return self.getlist(section, option, self._convert_to_boolean,
                        raw=raw, vars=vars, fallback=fallback, **kwargs)
        

        附言请记住缩进的重要性。如 ConfigParser 文档字符串中所述:

        值可以跨越多行,只要它们缩进更深 比值的第一行。根据解析器的模式,空白 行可能被视为多行值的一部分或被忽略。

        【讨论】:

          【解决方案15】:

          所以我更喜欢的另一种方法是拆分值,例如:

          #/path/to/config.cfg
          [Numbers]
          first_row = 1,2,4,8,12,24,36,48
          

          可以这样加载到字符串或整数列表中,如下:

          import configparser
          
          config = configparser.ConfigParser()
          config.read('/path/to/config.cfg')
          
          # Load into a list of strings
          first_row_strings = config.get('Numbers', 'first_row').split(',')
          
          # Load into a list of integers
          first_row_integers = [int(x) for x in config.get('Numbers', 'first_row').split(',')]
          

          此方法使您无需将值括在括号中以作为 JSON 加载。

          【讨论】:

          • 嗨,米奇,在后一种情况下,使用 get_int('first_row').split(',') 而不是在循环时将其显式转换为 int 会更好吗?
          • @Guido - 你试过你的建议了吗?你的意思是:first_row_integers = [x for x in config.getint('Numbers', 'first_row').split(',')]?它给出了一个错误:ValueError: invalid literal for int() with base 10: '1,2,4,8,12,24,36,48'
          • @Greenonline 是的,现在我不记得我在哪里使用它了,但可以肯定的是我最后一定没有使用split,否则这意味着我提供了string 而我没有立即解析为整数。
          【解决方案16】:

          你可以在配置文件中使用列表,然后在 python 中解析它

          from ast import literal_eval
          
          literal_eval("[1,2,3,4]")
          
          import json
          
          json.loads("[1,2,3,4]")
          

          你也可以像这样在你的配置文件后面使用 json 文件:

          your config file :
          [A]
          json_dis = .example.jason
          --------------------
          your code :
          import configparser
          config = configparser.ConfigParser()
          config.read('config.ini')
          # getting items of section A
          config.items('A')
          # result is a list of key-values
          

          【讨论】:

            【解决方案17】:

            为了更进一步地了解 Grr 的答案(我的最爱),您可以使用 map 函数,而不是在 .ini 文件中用引号将列表项括起来。这允许您以 Python 方式指定列表项数据类型。

            配置文件:

            [section]
            listKey1: 1001, 1002, 1003
            listKey2: AAAA, BBBB, CCCC
            

            代码:

            cfgFile = 'config.ini'
            parser = ConfigParser(converters={'list': lambda x: [i.strip() for i in x.split(',')]})
            parser.read(cfgFile)
            
            list1 = list(map(int, parser.getlist('section', 'listKey1')))
            list2 = list(map(str, parser.getlist('section', 'listKey2')))
            
            print(list1)
            print(list2)
            

            输出:

            [1001, 1002, 1003]
            ['AAAA', 'BBBB', 'CCCC']
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-03-10
              • 2021-04-06
              • 2023-03-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多