ConfigParser中的列表


Answers:


141

没有什么可以阻止您将列表打包为定界字符串,然后从配置中获取字符串后再将其解包。如果您采用这种方式,则您的config部分如下所示:

[Section 3]
barList=item1,item2

它虽然不漂亮,但对于大多数简单列表来说都可以使用。


2
而且,如果您有复杂的列表,则可以参考以下问题:stackoverflow.com/questions/330900/… :-)
John Fouhy

很好的解决方案,但是如果没有可能的分隔符(超过您可以保证的分隔符)怎么办将不会出现在列表项中???
2012年

@wim看看我的回答,您可以使用\ n作为分隔符
Peter Smit

@wim如果可能是合法字符,则需要实现一种转义分隔符的方法。(以及一种逃脱用于转义的任何字符的方法。)
jamesdlin

如果列表只有一个元素怎么办?
塞尔吉奥马夫拉

222

也有点晚,但可能对某些人有所帮助。我正在使用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]节非常方便。


1
这很棒,因为它会自动“投射”值,如果您事先不知道类型,该值将很有用。
LeGBT

我喜欢这个主意,但只能将其与数字列表配合使用。引号没有帮助。奇怪的。继续。
rsaw 2015年

5
您必须具有[“ a”,“ b”,“ c”]才能使字符串起作用。对我来说,这是单击数字的,但是由于cfg文件大多数都是可编辑的-每次添加“”都很麻烦。我宁愿使用逗号然后将其拆分。
Saurabh Hirani 2015年

仅使用标准库的优雅解决方案。很高兴能够使用注释和json。
wi1

例如,这对于原始字符串将如何工作key5 : [r"abc $x_i$", r"def $y_j$"]?他们提出了错误json.decoder.JSONDecodeError: Expecting value: line 1 column 2 (char 1)
kingusiu

100

这次聚会来晚了,但是我最近在配置文件中的专用部分实现了此功能:

[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

希望这可以帮助其他人谷歌搜索这个问题;)


3
我喜欢这种解决方案,因为您可以; comment从列表中删除某些项目,而不必重写整个列表。
2012年

1
+1,但如果要执行此操作,请谨慎使用key,因为ConfigParser会将所有此类键都转换为小写字母
Alex Dean

4
@AlexDean您可以通过设置optionxform = str来设置ConfigParser以将camelCase保留在原位。示例: config = ConfigParser.SafeConfigParser() config.optionxform = str 此案将被搁置
Cameron Goodale 2012年

@Henry Cooke您是否测试过多次列出密钥?
DevPlayer

1
@DevPlayer使用多键时,您只会得到最后一个值。(响应2年的旧评论以使其他读者受益)
Marcin K

62

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

;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

资源

我自己,如果这对您来说很常见,我可能会扩展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()?使用每个的默认行为,拆分显然更好(过滤出空白行)。除非我错过了某件事...
rsaw 2015年

7
.split()在所有空白处中断(除非给出了特定字符),. splitlines()在所有换行符处中断。
彼得·史密特

很好。我没有考虑这一点,因为我的价值观都没有空格。
rsaw 2015年

38

如果您想逐字传递列表,则可以使用:

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"]

在这种情况下,ast.literal_eval()与使用(可能更受欢迎)相比,使用的优势是什么json.loads()?我认为后者提供了更高的安全性,不是吗?
RayLuo

2
我希望看到和举例说明,如果您认为有帮助,可以随时在此主题中添加答案,尽管您的评论本身会是一个很好的问题。我给出的答案简化了ConfigParser中列表的使用,因此在应用程序内部是消除了使用正则表达式的麻烦。没有上下文,我无法评论其“安全”价值。
PythonTester

我要小心使用literal_eval,它期望在=或:之后加上python字符串,因此您不能再使用,例如path1 = / some / path /但path1 ='/ some / path /'
vldbnc

21

没有提及converterskwargConfigParser()这些答案中是令人失望的。

根据文档,您可以将字典传递给该字典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


15

我降落在这里试图消耗这个...

[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的问题,但可能是某些人正在寻找的简单答案。


2
我以为迪克在sorger@espionage.su!难怪我的邮件不断跳动!> _ <
奥古斯塔

1
4年后阅读此评论并嘲笑复活节彩蛋
一位好奇的工程师

11

这是我用于列表的内容:

配置文件内容:

[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
ashley

5

因此,我更喜欢的另一种方法是只拆分值,例如:

#/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加载。


嗨Mitch,在后一种情况下,使用get_int('first_row')。split(',')而不是在循环时将其显式转换为int会更好吗?
Guido

2

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


感谢您的澄清,utku。唯一的问题是我目前无法使用外部软件包。我想我要写一个简单的类来处理这个问题。我最终会分享。
pistacchio,

您正在运行什么版本的Python?JSON模块包含在2.6中。
帕特里克·哈灵顿

2

我过去也遇到过同样的问题。如果需要更复杂的列表,请考虑通过从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 ]

但是,我正在考虑这样做:为什么不将config的值设置为like barList=item1,item2,然后调用if value.find(',') > 0: return value.split(','),或者更好的是,让应用程序将所有的config选项解析为列表,而.split(',')盲目地解析所有内容?
Droogans 2012年

1
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'

这是用于非常简单的配置的快速解析,您将失去获取int,bool和其他类型的输出的所有功能,而无需转换从返回的对象Parser或重新执行由Parser类在其他地方完成的解析工作。


1

我在项目中用没有值的键完成了类似的任务:

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

0

json.loadsast.literal_eval似乎有效,但config中的简单列表将每个字符视为字节,因此甚至返回方括号...。

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

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


0

如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文档字符串中读取的:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.