3

モジュールの動作を模倣してConfigParser、使用する特定のアプリケーションの構成ファイル内の明確に定義された構造を活用する、高度に専門化されたパーサーを作成しています。構成ファイルのいくつかのセクションには、次のように、Variable_またはのいずれかで始まる数百の変数とルーチンのマッピングが含まれています。Routine_

[Map.PRD]
Variable_FOO=LOC1
Variable_BAR=LOC2
Routine_FOO=LOC3
Routine_BAR=LOC4
...

[Map.SHD]
Variable_FOO=LOC1
Variable_BAR=LOC2
Routine_FOO=LOC3
Routine_BAR=LOC4
...

各セクションが単一の辞書として格納される基本構造を維持したいConfigParserので、ユーザーは引き続き従来の構文にアクセスできます。

config.content['Mappings']['Variable_FOO'] = 'LOC1'

このセクションにドリルダウンする簡略化された API を使用することもできます。

config.vmapping('PRD')['FOO'] = 'LOC1'
config.vmapping('PRD')['BAR'] = 'LOC2'
config.rmapping('PRD')['FOO'] = 'LOC3'
config.rmapping('PRD')['BAR'] = 'LOC4'

現在、属性dictを追加した特別なサブクラスにセクションを格納することで、これを実装しています。パーサーprefixvariableおよびプロパティは、 のようなオブジェクトの属性をorに設定し、次に変更されたハンドルの属性は、適切なアイテムにアクセスするためのキーとプレフィックスを結び付けます。それは機能していますが、反復のサポートなど、関連するすべての機能を実装するために多くのボイラープレートが必要です。routineprefixdict'Variable_''Routine_'__getitem____setitem__dict

私の理想的な解決策は、サブクラスdict化されたものを省き、variableとプロパティがプレフィックスなしで下にroutineあるプレーンなオブジェクトの「ビュー」を何らかの方法で提示することだと思います。dict

アップデート

主に@abarnetの回答に基づいて、私が実装したソリューションを次に示します。

class MappingDict(object):
    def __init__(self, prefix, d):
        self.prefix, self.d = prefix, d
    def prefixify(self, name):
        return '{}_{}'.format(self.prefix, name)
    def __getitem__(self, name):
        name = self.prefixify(name)
        return self.d.__getitem__(name)
    def __setitem__(self, name, value):
        name = self.prefixify(name)
        return self.d.__setitem__(name, value)
    def __delitem__(self, name):
        name = self.prefixify(name)
        return self.d.__delitem__(name)
    def __iter__(self):
        return (key.partition('_')[-1] for key in self.d
                if key.startswith(self.prefix))
    def __repr__(self):
        return 'MappingDict({})'.format(dict.__repr__(self))

class MyParser(object):
    SECTCRE = re.compile(r'\[(?P<header>[^]]+)\]')
    def __init__(self, filename):
        self.filename = filename
        self.content = {}
        lines = [x.strip() for x in open(filename).read().splitlines() 
                 if x.strip()]
        for line in lines:
            match = re.match(self.SECTCRE, line)
            if match:
                section = match.group('header')
                self.content[section] = {}
            else:
                key, sep, value = line.partition('=')
                self.content[section][key] = value
    def write(self, filename):
        fp = open(filename, 'w')
        for section in sorted(self.content, key=sectionsort):
            fp.write("[%s]\n" % section)
            for key in sorted(self.content[section], key=cpfsort):
                value = str(self.content[section][key])
                fp.write("%s\n" % '='.join([key,value]))
            fp.write("\n")
        fp.close()
    def vmapping(self, nsp):
        section = 'Map.{}'.format(nsp)
        return MappingDict('Variable', self.content[section])
    def rmapping(self, nsp):
        section = 'Map.{}'.format(nsp)
        return MappingDict('Routine', self.content[section])

次のように使用されます。

config = MyParser('myfile.cfg')
vmap = config.vmapping('PRD')
vmap['FOO'] = 'LOC5'
vmap['BAR'] = 'LOC6'
config.write('newfile.cfg')

結果はとの変更newfile.cfgを反映します。LOC5LOC6

4

2 に答える 2

3

ここで継承が必要だとは思いません。dictロード時に作成し、保存時に一緒に貼り付けなければならない 2 つの別個のオブジェクトができあがります…</p>

それが許容できる場合は、通常の操作中にプレフィックスを気にする必要さえありません。次のように、保存中にプレフィックスを付けるだけです。

class Config(object):
    def save(self):
        merged = {'variable_{}'.format(key): value for key, value 
                  in self.variable_dict.items()}
        merged.update({'routine_{}'.format(key): value for key, value 
                       in self.routine_dict.items()}
        # now save merged

mergedそのオブジェクトを常に表示したいが、それほど頻繁に呼び出されるとは思わない場合は、 @property.

辞書に定期的にアクセスしたい場合merged、同時に 2 つのサブ辞書にアクセスしている場合は、はい、ビューが必要です。

私の理想的な解決策は、サブクラス化された dict を省き、グローバルおよびルーチン プロパティに、接頭辞なしでプレーンな dict オブジェクトの「ビュー」を何らかの方法で提示させることだと思います。

これを継承で行うのは非常に困難です。確かにdict;からの継承ではありません。Python 3 を使用している場合はからの継承builtins.dict_itemsが機能する可能性がありますが、それでもまだ無理があるようです。

しかし、委任を使えば簡単です。各サブディクショナリは、親への参照を保持するだけdictです:

class PrefixedDict(object):
    def __init__(self, prefix, d):
        self.prefix, self.d = prefix, d
    def prefixify(self, key):
        return '{}_{}'.format(self.prefix, key)
    def __getitem__(self, key):
        return self.d.__getitem__(self.prefixify(key))
    def __setitem__(self, key, value):
        return self.d.__setitem__(self.prefixify(key), value)
    def __delitem__(self, key):
        return self.d.__delitem__(self.prefixify(key))
    def __iter__(self):
        return (key[len(self.prefix):] for key in self.d 
                if key.startswith(self.prefix)])

その方法では無料でメソッドを入手することはできませんが、dictそれは良いことです。とにかく、ほとんどが間違っていたからです。必要なものを明示的に委任します。(そのまま通過させたいものがある場合__getattr__はそちらをご利用ください。)

概念的に単純で、誤って何かをオーバーライドするのを忘れて失敗するのが難しくなるPrefixDictだけでなく、 がdict.


では、どの方法をとっても、これらのオブジェクトはどこでどのように作成されるのでしょうか?

簡単な答えは、それらは を構築するときに作成する属性であるということですConfig:

def __init__(self):
    self.d = {}
    self.variable = PrefixedDict('Variable', self.d)
    self.routine = PrefixedDict('Routine', self.d)

これを動的にする必要がある場合 (たとえば、任意のプレフィックスのセットが存在する可能性がある場合)、ロード時にそれらを作成します。

def load(self):
    # load up self.d
    prefixes = set(key.split('_')[0] for key in self.d)
    for prefix in prefixes:
        setattr(self, prefix, PrefixedDict(prefix, self.d)

その場でそれらを作成できるようにしたい場合 (config.newprefix['foo'] = 3追加します'Newprefix_foo')、代わりにこれを行うことができます:

def __getattr__(self, name):
    return PrefixedDict(name.title(), self.d)

しかし、動的属性を使用するようになると、代わりにconfig['newprefix']['foo']. 一つには、元の質問のように、実際に subdictionaries の 1 つを呼び出すことができます'global'…</p>

または、最初にディクショナリ構文を構築し、通常はattrdict(ActiveState レシピと 3000 実装の PyPI を検索してください…) と呼ばれるものを使用して、自動的にconfig.newprefixmeanconfig['newprefix']を作成できるようにすることで、有効な識別子がある場合に属性構文を使用できますが、フォールバックできます。そうでない場合は辞書構文に。

于 2013-01-10T02:22:56.293 に答える
0

続行する方法にはいくつかのオプションがあります。

最も簡単なのはネストされた辞書を使用することかもしれないので、にVariable_FOOなりconfig["variable"]["FOO"]ます。defaultdict(dict)最初の値を追加するときに内部ディクショナリを初期化することを心配する必要がないように、外部ディクショナリにaを使用することをお勧めします。

もう1つのオプションは、単一の辞書でタプルキーを使用することです。つまり、Variable_FOOになりconfig[("variable", "FOO")]ます。に割り当てることができるので、これはコードで簡単に行うことができますconfig[tuple(some_string.split("_"))]。ただし、この場合は、分割されていない文字列をキーとして使用することもできると思います。

最後のアプローチでは、舞台裏を使用して、必要な構文(Variable_FOOとしてアクセスされる場所config.Variable["FOO"])を使用できます。__getattr__defaultdict

from collections import defaultdict

class Config(object):
    def __init__(self):
        self._attrdicts = defaultdict(dict)

    def __getattr__(self, name):
        return self._attrdicts[name]

の動作でこれを拡張できますが、おそらく必要__setattr__あり__delattr__ません。このアプローチの唯一の重大な制限は(質問の元のバージョンを考えると)、属性名(のようなVariable)は正当なPython識別子でなければならないということです。global先頭に数字が付いた文字列、Pythonキーワード(など)、または空白文字を含む文字列は使用できません。

このアプローチの欠点は、プログラムで使用するのが少し難しいことです(たとえば、構成ファイルパーサーによって)。の値を読み取ってVariable_FOO保存するには、次のようなグローバル関数config.Variable["FOO"]を使用する必要があります。getattr

 name, value = line.split("=")
 prefix, suffix = name.split("_")
 getattr(config, prefix)[suffix] = value
于 2013-01-10T02:50:42.823 に答える