ConfigParserのリスト


回答:


142

リストを区切られた文字列にパックし、構成から文字列を取得したら、それをアンパックすることを妨げるものは何もありません。このようにすると、configセクションは次のようになります。

[Section 3]
barList=item1,item2

きれいではありませんが、ほとんどの単純なリストで機能します。


2
そして、複雑なリストがある場合は、この質問を参照できます:stackoverflow.com/questions/330900/… :-)
John Fouhy

素晴らしい解決策ですが、リストアイテム内に表示されないことを保証できる区切り文字がない場合にどうすればいいですか?
WIM

@wim私の答えを参照してください。区切り文字として\ nを使用できます
Peter Smit

@wim区切り文字が有効な文字である可能性がある場合は、区切り文字をエスケープする方法を実装する必要があります。(そして、エスケープに使用するすべてのキャラクターをエスケープする方法。)
jamesdlin 2017

リストに単一の要素がある場合はどうなりますか?
セルジオマフラ

223

また少し遅れますが、一部の人にとっては役立つかもしれません。私は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 2014年

このアイデアは大好きですが、数値のリストでしか機能しません。引用符は役に立ちません。変だ。次へ。
rsaw 2015年

5
文字列が機能するためには、["a"、 "b"、 "c"]が必要です。私にとって、これはクリック数ですが、cfgファイルはほとんど編集可能であるため、毎回「」を追加するのは面倒です。私はむしろコンマを使用してからそれを分割したいと思います。
Saurabh Hirani 2015

標準ライブラリのみを使用したエレガントなソリューション。コメントとjsonを使用できるようになりました。
wi1 2018

これは生の文字列に対してどのように機能しkey5 : [r"abc $x_i$", r"def $y_j$"]ますか?彼らはエラーを上げるjson.decoder.JSONDecodeError: Expecting value: line 1 column 2 (char 1)
kingusiu

101

このパーティーには遅くなりましたが、最近、リストの構成ファイルの専用セクションを使用してこれを実装しました。

[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リスト全体を書き直さなくてもリストから特定の項目を取り出すことができるので、このソリューションが気に入っています。
WIM

1
+1、ただしこれを行う場合keyは、ConfigParserがそのようなすべてのキーを小文字に変換するため、も使用することに注意してください
Alex Dean

4
@AlexDean optionxform = strを設定することにより、キャメルケースをそのままにしておくようにConfigParserを設定できます。例: config = ConfigParser.SafeConfigParser() config.optionxform = str その後、ケースはそのままになります
Cameron Goodale

@ヘンリー・クックキーが複数回リストされている場合、それをテストしましたか?
DevPlayer 2016年

1
@DevPlayerマルチキーを使用すると、最後の値しか取得できません。(他の読者の利益のための2歳のコメントに対応)
Marcin K

63

多くの人が知らないことの1つは、複数行の構成値が許可されていることです。例えば:

;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()ですか?それぞれのデフォルトの動作を使用すると、splitは明らかに優れています(空白行をフィルターで除外します)。私が何かを見逃していない限り...
rsaw

7
.split()はすべての空白で改行し(特定の文字が指定されていない場合)、. splitlines()はすべての改行文字で改行します。
Peter Smit 2015年

ああ良い点。私の値にはスペースがなかったので、それについては考えませんでした。
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 2016年

私は後にPythonの文字列を期待literal_evalを使用して注意が必要=かになりますので、あなたはもう使用できないなどのpath1 = /いくつかの/パス/パス1が、=「/いくつかの/パス/」
vldbnc

21

のためのクワーグについての言及なしconvertersConfigParser()これらの回答のいずれはなく、むしろがっかりしたものでした。

ドキュメントによると、パーサーとセクションプロキシの両方にメソッドを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感謝を探していたということです
アシュリー

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としてロードするために値を角括弧で囲む必要がなくなります。


こんにちはミッチ、後者の場合、ループ中に明示的にintに変換する代わりに、get_int( 'first_row')。split( '、')を使用する方がいいでしょう。
Guido

2

構成パーサーによる直列化では、プリミティブ型のみがサポートされています。この種の要件には、JSONまたはYAMLを使用します。


説明をありがとう、utku。唯一の問題は、現時点では外部パッケージを使用できないことです。これを処理する簡単なクラスを書くつもりです。最終的には共有します。
ピスタッキオ2008

実行しているPythonのバージョンは何ですか?JSONモジュールは2.6に含まれています。
Patrick Harrington、

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 ]

しかし、私はこれを行うことを考えていました:のように設定値を設定してbarList=item1,item2からを呼び出さないのはなぜですかif value.find(',') > 0: return value.split(',')、またはさらに良いことに、アプリケーションにすべての設定オプションをリストとして解析させ、.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ます。


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内の単純なリストは各文字をバイトとして扱っているため、角括弧も返します。

構成がある場合の意味 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)

Psはインデントの重要性を覚えています。ConfigParser doc文字列を読み取ると、次のようになります。

値は、値の最初の行よりもインデントされている限り、複数行にまたがることができます。パーサーのモードによっては、空白行が複数行の値の一部として扱われるか、無視される場合があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.