web-dev-qa-db-ja.com

ネストされた辞書を実装する最良の方法は何ですか?

基本的にネストされた辞書に相当するデータ構造があります。このように見えるとしましょう:

{'new jersey': {'mercer county': {'plumbers': 3,
                                  'programmers': 81},
                'middlesex county': {'programmers': 81,
                                     'salesmen': 62}},
 'new york': {'queens county': {'plumbers': 9,
                                'salesmen': 36}}}

現在、これを維持および作成するのは非常に苦痛です。新しい州/郡/職業を持つたびに、不快なtry/catchブロックを介して下位層の辞書を作成する必要があります。さらに、すべての値を調べたい場合は、迷惑なネストされたイテレーターを作成する必要があります。

次のようなタプルをキーとして使用することもできます。

{('new jersey', 'mercer county', 'plumbers'): 3,
 ('new jersey', 'mercer county', 'programmers'): 81,
 ('new jersey', 'middlesex county', 'programmers'): 81,
 ('new jersey', 'middlesex county', 'salesmen'): 62,
 ('new york', 'queens county', 'plumbers'): 9,
 ('new york', 'queens county', 'salesmen'): 36}

これにより、値の反復が非常に簡単かつ自然になりますが、集計や辞書のサブセットの確認などの操作を行うのは、構文的に苦痛です(たとえば、状態ごとに移動する場合)。

基本的に、ネストされた辞書をフラットな辞書と考えることもあれば、実際に複雑な階層と考えることもあります。これをすべてクラスでラップすることもできますが、すでに誰かがこれを行っているようです。あるいは、これを行うためのいくつかの本当にエレガントな構文構造があるかもしれません。

どうすればこれを改善できますか?

補遺:私はsetdefault()を知っていますが、それは本当にきれいな構文を作りません。また、作成する各サブ辞書には、setdefault()を手動で設定する必要があります。

193
YGA

Pythonでネストされた辞書を実装する最良の方法は何ですか?

dictサブクラスに__missing__を実装して、新しいインスタンスを設定して返します。

このアプローチは利用可能です (およびドキュメント化された) Python 2.5以降、および(特に私にとって価値がある)それは通常の辞書のようにきれいに印刷されます、autovivified defaultdictのい印刷の代わりに:

class Vividict(dict):
    def __missing__(self, key):
        value = self[key] = type(self)() # retain local pointer to value
        return value                     # faster to return than dict lookup

(注self[key]は割り当ての左側にあるため、ここには再帰はありません。)

そして、あなたはいくつかのデータを持っていると言います:

data = {('new jersey', 'mercer county', 'plumbers'): 3,
        ('new jersey', 'mercer county', 'programmers'): 81,
        ('new jersey', 'middlesex county', 'programmers'): 81,
        ('new jersey', 'middlesex county', 'salesmen'): 62,
        ('new york', 'queens county', 'plumbers'): 9,
        ('new york', 'queens county', 'salesmen'): 36}

使用コードは次のとおりです。

vividict = Vividict()
for (state, county, occupation), number in data.items():
    vividict[state][county][occupation] = number

そしていま:

>>> import pprint
>>> pprint.pprint(vividict, width=40)
{'new jersey': {'mercer county': {'plumbers': 3,
                                  'programmers': 81},
                'middlesex county': {'programmers': 81,
                                     'salesmen': 62}},
 'new york': {'queens county': {'plumbers': 9,
                                'salesmen': 36}}}

批判

このタイプのコンテナに対する批判は、ユーザーがキーのスペルを間違えた場合、コードが黙って失敗する可能性があるということです。

>>> vividict['new york']['queens counyt']
{}

さらに、データにスペルミスのある郡があることになります。

>>> pprint.pprint(vividict, width=40)
{'new jersey': {'mercer county': {'plumbers': 3,
                                  'programmers': 81},
                'middlesex county': {'programmers': 81,
                                     'salesmen': 62}},
 'new york': {'queens county': {'plumbers': 9,
                                'salesmen': 36},
              'queens counyt': {}}}

説明:

キーがアクセスされたが見つからない場合は、クラスVividictの別のネストされたインスタンスを提供するだけです。 (値の割り当てを返すことは、dictでゲッターをさらに呼び出すことを回避するので便利ですが、残念ながら、設定されているとおりに返すことはできません。)

注意してください、これらは最も賛成の答えと同じセマンティクスですが、コードの半分の行-noskloの実装:

class AutoVivification(dict):
    """Implementation of Perl's autovivification feature."""
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            value = self[item] = type(self)()
            return value

使用法のデモンストレーション

以下は、このdictを使用して、オンザフライでネストされたdict構造を簡単に作成する方法の例です。これにより、階層ツリー構造を思い通りにすばやく作成できます。

import pprint

class Vividict(dict):
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

d = Vividict()

d['foo']['bar']
d['foo']['baz']
d['fizz']['buzz']
d['primary']['secondary']['tertiary']['quaternary']
pprint.pprint(d)

どの出力:

{'fizz': {'buzz': {}},
 'foo': {'bar': {}, 'baz': {}},
 'primary': {'secondary': {'tertiary': {'quaternary': {}}}}}

そして最後の行が示すように、手作業で検査するためにきれいに印刷されます。ただし、データを視覚的に検査する場合は、__missing__を実装して、そのクラスの新しいインスタンスをキーに設定し、それを返すと、はるかに優れたソリューションになります。

対照的に、他の選択肢:

dict.setdefault

質問者はこれはきれいではないと考えていますが、私はVividictよりも望ましいと思います。

d = {} # or dict()
for (state, county, occupation), number in data.items():
    d.setdefault(state, {}).setdefault(county, {})[occupation] = number

そしていま:

>>> pprint.pprint(d, width=40)
{'new jersey': {'mercer county': {'plumbers': 3,
                                  'programmers': 81},
                'middlesex county': {'programmers': 81,
                                     'salesmen': 62}},
 'new york': {'queens county': {'plumbers': 9,
                                'salesmen': 36}}}

スペルミスは騒々しく失敗し、データに悪い情報が散らかることはありません。

>>> d['new york']['queens counyt']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'queens counyt'

さらに、ループで使用するとsetdefaultがうまく機能し、キーに対して何を取得するかわからないが、繰り返し使用するのは非常に面倒になり、誰もが次のことを続けたいとは思わない:

d = dict()

d.setdefault('foo', {}).setdefault('bar', {})
d.setdefault('foo', {}).setdefault('baz', {})
d.setdefault('fizz', {}).setdefault('buzz', {})
d.setdefault('primary', {}).setdefault('secondary', {}).setdefault('tertiary', {}).setdefault('quaternary', {})

もう1つの批判は、setdefaultを使用するかどうかに関係なく、新しいインスタンスが必要だということです。ただし、Python(または少なくともCPython)は、未使用および参照されていない新しいインスタンスの処理についてかなり賢明です。たとえば、メモリ内の場所を再利用します。

>>> id({}), id({}), id({})
(523575344, 523575344, 523575344)

自動生成されたdefaultdict

これは見栄えの良い実装であり、データを検査していないスクリプトでの使用は、__missing__の実装と同じくらい便利です。

from collections import defaultdict

def vivdict():
    return defaultdict(vivdict)

ただし、データを検査する必要がある場合は、同じようにデータが自動生成されたdefaultdictの結果は次のようになります。

>>> d = vivdict(); d['foo']['bar']; d['foo']['baz']; d['fizz']['buzz']; d['primary']['secondary']['tertiary']['quaternary']; import pprint; 
>>> pprint.pprint(d)
defaultdict(<function vivdict at 0x17B01870>, {'foo': defaultdict(<function vivdict 
at 0x17B01870>, {'baz': defaultdict(<function vivdict at 0x17B01870>, {}), 'bar': 
defaultdict(<function vivdict at 0x17B01870>, {})}), 'primary': defaultdict(<function 
vivdict at 0x17B01870>, {'secondary': defaultdict(<function vivdict at 0x17B01870>, 
{'tertiary': defaultdict(<function vivdict at 0x17B01870>, {'quaternary': defaultdict(
<function vivdict at 0x17B01870>, {})})})}), 'fizz': defaultdict(<function vivdict at 
0x17B01870>, {'buzz': defaultdict(<function vivdict at 0x17B01870>, {})})})

この出力は非常に洗練されておらず、結果はまったく読めません。通常与えられる解決策は、手動検査のために再帰的に辞書に変換することです。この重要なソリューションは、読者の演習として残しておきます。

性能

最後に、パフォーマンスを見てみましょう。インスタンス化のコストを差し引いています。

>>> import timeit
>>> min(timeit.repeat(lambda: {}.setdefault('foo', {}))) - min(timeit.repeat(lambda: {}))
0.13612580299377441
>>> min(timeit.repeat(lambda: vivdict()['foo'])) - min(timeit.repeat(lambda: vivdict()))
0.2936999797821045
>>> min(timeit.repeat(lambda: Vividict()['foo'])) - min(timeit.repeat(lambda: Vividict()))
0.5354437828063965
>>> min(timeit.repeat(lambda: AutoVivification()['foo'])) - min(timeit.repeat(lambda: AutoVivification()))
2.138362169265747

パフォーマンスに基づいて、dict.setdefaultが最適に機能します。実行速度が気になる場合は、本番コードに強くお勧めします。

(おそらくIPythonノートブックで)インタラクティブな使用にこれが必要な場合、パフォーマンスはそれほど重要ではありません。その場合、出力の可読性のためにVividictを使用します。 AutoVivificationオブジェクト(この目的のために作成された__getitem__の代わりに__missing__を使用)と比較すると、はるかに優れています。

結論

サブクラス化されたdict__missing__を実装して、新しいインスタンスを設定して返すことは、代替案よりもやや困難ですが、次の利点があります。

  • 簡単なインスタンス化
  • 簡単なデータ入力
  • 簡単なデータ表示

また、__getitem__を変更するよりも複雑でなくパフォーマンスが高いため、その方法よりも優先されるべきです。

それにもかかわらず、欠点があります:

  • 不正な検索はサイレントに失敗します。
  • 不適切なルックアップは辞書に残ります。

したがって、私は個人的にsetdefaultを他のソリューションよりも好み、あらゆる状況でこの種の振る舞いを必要としていた。

165
Aaron Hall
class AutoVivification(dict):
    """Implementation of Perl's autovivification feature."""
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            value = self[item] = type(self)()
            return value

テスト:

a = AutoVivification()

a[1][2][3] = 4
a[1][3][3] = 5
a[1][2]['test'] = 6

print a

出力:

{1: {2: {'test': 6, 3: 4}, 3: {3: 5}}}
187
nosklo

こんなに小さいものを見たことがなかったからといって、ここにあなたが好きなように入れ子になった辞書があります。

# yo dawg, i heard you liked dicts                                                                      
def yodict():
    return defaultdict(yodict)
31
paint can

YAMLファイルを作成し、 PyYaml を使用して読み込むことができます。

ステップ1:YAMLファイル「employee.yml」を作成します。

new jersey:
  mercer county:
    pumbers: 3
    programmers: 81
  middlesex county:
    salesmen: 62
    programmers: 81
new york:
  queens county:
    plumbers: 9
    salesmen: 36

ステップ2:Pythonで読む

import yaml
file_handle = open("employment.yml")
my_shnazzy_dictionary = yaml.safe_load(file_handle)
file_handle.close()

これで、my_shnazzy_dictionaryにすべての値が設定されました。オンザフライでこれを行う必要がある場合、YAMLを文字列として作成し、yaml.safe_load(...)にフィードすることができます。

24
Pete

スタースキーマデザインがあるため、リレーショナルテーブルではなく辞書のように構造化することをお勧めします。

import collections

class Jobs( object ):
    def __init__( self, state, county, title, count ):
        self.state= state
        self.count= county
        self.title= title
        self.count= count

facts = [
    Jobs( 'new jersey', 'mercer county', 'plumbers', 3 ),
    ...

def groupBy( facts, name ):
    total= collections.defaultdict( int )
    for f in facts:
        key= getattr( f, name )
        total[key] += f.count

このようなことは、SQLのオーバーヘッドなしでデータウェアハウスのような設計を作成するのに大いに役立ちます。

18
S.Lott

ネストレベルの数が少ない場合は、collections.defaultdictを使用します。

from collections import defaultdict

def nested_dict_factory(): 
  return defaultdict(int)
def nested_dict_factory2(): 
  return defaultdict(nested_dict_factory)
db = defaultdict(nested_dict_factory2)

db['new jersey']['mercer county']['plumbers'] = 3
db['new jersey']['mercer county']['programmers'] = 81

このようにdefaultdictを使用すると、厄介なsetdefault()get()などを大量に回避できます。

13
user26294

これは、任意の深さのネストされた辞書を返す関数です。

from collections import defaultdict
def make_dict():
    return defaultdict(make_dict)

次のように使用します。

d=defaultdict(make_dict)
d["food"]["meat"]="beef"
d["food"]["veggie"]="corn"
d["food"]["sweets"]="ice cream"
d["animal"]["pet"]["dog"]="collie"
d["animal"]["pet"]["cat"]="tabby"
d["animal"]["farm animal"]="chicken"

次のようなものですべてを繰り返します:

def iter_all(d,depth=1):
    for k,v in d.iteritems():
        print "-"*depth,k
        if type(v) is defaultdict:
            iter_all(v,depth+1)
        else:
            print "-"*(depth+1),v

iter_all(d)

これは印刷します:

- food
-- sweets
--- ice cream
-- meat
--- beef
-- veggie
--- corn
- animal
-- pet
--- dog
---- labrador
--- cat
---- tabby
-- farm animal
--- chicken

最終的には、新しい項目を辞書に追加できないようにすることをお勧めします。これらのすべてのdefaultdictsを通常のdictsに再帰的に変換するのは簡単です。

def dictify(d):
    for k,v in d.iteritems():
        if isinstance(v,defaultdict):
            d[k] = dictify(v)
    return dict(d)
9
JnBrymn

setdefaultは非常に便利です。キーが存在するかどうかを確認し、存在しない場合は追加します。

d = {}
d.setdefault('new jersey', {}).setdefault('mercer county', {})['plumbers'] = 3

setdefaultは常に関連するキーを返すため、実際には 'd'の値を適切に更新しています。

反復処理に関しては、Pythonにまだ存在しないジェネレーターを簡単に作成できると確信しています。

def iterateStates(d):
    # Let's count up the total number of "plumbers" / "dentists" / etc.
    # across all counties and states
    job_totals = {}

    # I guess this is the annoying nested stuff you were talking about?
    for (state, counties) in d.iteritems():
        for (county, jobs) in counties.iteritems():
            for (job, num) in jobs.iteritems():
                # If job isn't already in job_totals, default it to zero
                job_totals[job] = job_totals.get(job, 0) + num

    # Now return an iterator of (job, number) tuples
    return job_totals.iteritems()

# Display all jobs
for (job, num) in iterateStates(d):
    print "There are %d %s in total" % (job, num)
7
andygeers

他の人が示唆しているように、リレーショナルデータベースはあなたにとってより便利です。インメモリsqlite3データベースをデータ構造として使用して、テーブルを作成し、クエリを実行できます。

import sqlite3

c = sqlite3.Connection(':memory:')
c.execute('CREATE TABLE jobs (state, county, title, count)')

c.executemany('insert into jobs values (?, ?, ?, ?)', [
    ('New Jersey', 'Mercer County',    'Programmers', 81),
    ('New Jersey', 'Mercer County',    'Plumbers',     3),
    ('New Jersey', 'Middlesex County', 'Programmers', 81),
    ('New Jersey', 'Middlesex County', 'Salesmen',    62),
    ('New York',   'Queens County',    'Salesmen',    36),
    ('New York',   'Queens County',    'Plumbers',     9),
])

# some example queries
print list(c.execute('SELECT * FROM jobs WHERE county = "Queens County"'))
print list(c.execute('SELECT SUM(count) FROM jobs WHERE title = "Programmers"'))

これは単なる簡単な例です。州、郡、役職ごとに別々のテーブルを定義できます。

6

defaultdict()はあなたの友達です!

2次元の辞書の場合:

d = defaultdict(defaultdict)
d[1][2] = 3

より多くの次元については、次のことができます。

d = defaultdict(lambda :defaultdict(defaultdict))
d[1][2][3] = 4
5
Paula

collections.defaultdictをサブクラス化して、ネストされた辞書を作成できます。次に、有用な反復メソッドをそのクラスに追加します。

>>> from collections import defaultdict
>>> class nesteddict(defaultdict):
    def __init__(self):
        defaultdict.__init__(self, nesteddict)
    def walk(self):
        for key, value in self.iteritems():
            if isinstance(value, nesteddict):
                for tup in value.walk():
                    yield (key,) + tup
            else:
                yield key, value


>>> nd = nesteddict()
>>> nd['new jersey']['mercer county']['plumbers'] = 3
>>> nd['new jersey']['mercer county']['programmers'] = 81
>>> nd['new jersey']['middlesex county']['programmers'] = 81
>>> nd['new jersey']['middlesex county']['salesmen'] = 62
>>> nd['new york']['queens county']['plumbers'] = 9
>>> nd['new york']['queens county']['salesmen'] = 36
>>> for tup in nd.walk():
    print tup


('new jersey', 'mercer county', 'programmers', 81)
('new jersey', 'mercer county', 'plumbers', 3)
('new jersey', 'middlesex county', 'programmers', 81)
('new jersey', 'middlesex county', 'salesmen', 62)
('new york', 'queens county', 'salesmen', 36)
('new york', 'queens county', 'plumbers', 9)
5
A. Coady

「不快なtry/catchブロック」に関して:

d = {}
d.setdefault('key',{}).setdefault('inner key',{})['inner inner key'] = 'value'
print d

利回り

{'key': {'inner key': {'inner inner key': 'value'}}}

これを使用して、フラットな辞書形式から構造化形式に変換できます。

fd = {('new jersey', 'mercer county', 'plumbers'): 3,
 ('new jersey', 'mercer county', 'programmers'): 81,
 ('new jersey', 'middlesex county', 'programmers'): 81,
 ('new jersey', 'middlesex county', 'salesmen'): 62,
 ('new york', 'queens county', 'plumbers'): 9,
 ('new york', 'queens county', 'salesmen'): 36}

for (k1,k2,k3), v in fd.iteritems():
    d.setdefault(k1, {}).setdefault(k2, {})[k3] = v
4
vartec

Addictを使用できます: https://github.com/mewwts/addict

>>> from addict import Dict
>>> my_new_shiny_dict = Dict()
>>> my_new_shiny_dict.a.b.c.d.e = 2
>>> my_new_shiny_dict
{'a': {'b': {'c': {'d': {'e': 2}}}}}
4
JnBrymn

ネストされた辞書を簡単に反復処理するために、単純なジェネレーターを作成してみませんか?

def each_job(my_dict):
    for state, a in my_dict.items():
        for county, b in a.items():
            for job, value in b.items():
                yield {
                    'state'  : state,
                    'county' : county,
                    'job'    : job,
                    'value'  : value
                }

したがって、コンパイル済みのネストされた辞書がある場合、それを反復処理するのは簡単になります。

for r in each_job(my_dict):
    print "There are %d %s in %s, %s" % (r['value'], r['job'], r['county'], r['state'])

明らかに、ジェネレーターは、ユーザーにとって有用なデータ形式を生成できます。

なぜtry catchブロックを使用してツリーを読み取るのですか?キーを取得しようとする前に、ディクショナリにキーが存在するかどうかを照会するのは十分に簡単です(おそらく安全です)。ガード句を使用する関数は次のようになります。

if not my_dict.has_key('new jersey'):
    return False

nj_dict = my_dict['new jersey']
...

または、おそらく多少冗長な方法は、getメソッドを使用することです。

value = my_dict.get('new jersey', {}).get('middlesex county', {}).get('salesmen', 0)

しかし、より簡潔な方法として、 collections.defaultdict の使用を検討することをお勧めします。これは、python 2.5以降の標準ライブラリの一部です。

import collections

def state_struct(): return collections.defaultdict(county_struct)
def county_struct(): return collections.defaultdict(job_struct)
def job_struct(): return 0

my_dict = collections.defaultdict(state_struct)

print my_dict['new jersey']['middlesex county']['salesmen']

ここでは、データ構造の意味について想定していますが、実際にやりたいことを簡単に調整できるはずです。

3
SpoonMeiser

これをクラスでラップし、__getitem____setitem__を実装して、単純なクエリ言語を実装するというアイデアが好きです。

>>> d['new jersey/mercer county/plumbers'] = 3
>>> d['new jersey/mercer county/programmers'] = 81
>>> d['new jersey/mercer county/programmers']
81
>>> d['new jersey/mercer country']
<view which implicitly adds 'new jersey/mercer county' to queries/mutations>

あなたが空想を得たいなら、あなたは次のようなものを実装することもできます:

>>> d['*/*/programmers']
<view which would contain 'programmers' entries>

しかし、ほとんど私はそのようなことを実装するのは本当に楽しいだろうと思う:D

2
Aaron Maenpaa

私はこの機能を使用していました。その安全、迅速、簡単なメンテナンス。

def deep_get(dictionary, keys, default=None):
    return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary)

例:

>>> from functools import reduce
>>> def deep_get(dictionary, keys, default=None):
...     return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary)
...
>>> person = {'person':{'name':{'first':'John'}}}
>>> print (deep_get(person, "person.name.first"))
John
>>> print (deep_get(person, "person.name.lastname"))
None
>>> print (deep_get(person, "person.name.lastname", default="No lastname"))
No lastname
>>>
1
Yuda Prawira

データセットがかなり小さくならない限り、リレーショナルデータベースの使用を検討することをお勧めします。カウントを追加し、カウントのサブセットを選択し、さらにカウントを州、郡、職業、またはこれらの組み合わせごとに集計することも簡単にできます。

1
allyourcode
class JobDb(object):
    def __init__(self):
        self.data = []
        self.all = set()
        self.free = []
        self.index1 = {}
        self.index2 = {}
        self.index3 = {}

    def _indices(self,(key1,key2,key3)):
        indices = self.all.copy()
        wild = False
        for index,key in ((self.index1,key1),(self.index2,key2),
                                             (self.index3,key3)):
            if key is not None:
                indices &= index.setdefault(key,set())
            else:
                wild = True
        return indices, wild

    def __getitem__(self,key):
        indices, wild = self._indices(key)
        if wild:
            return dict(self.data[i] for i in indices)
        else:
            values = [self.data[i][-1] for i in indices]
            if values:
                return values[0]

    def __setitem__(self,key,value):
        indices, wild = self._indices(key)
        if indices:
            for i in indices:
                self.data[i] = key,value
        Elif wild:
            raise KeyError(k)
        else:
            if self.free:
                index = self.free.pop(0)
                self.data[index] = key,value
            else:
                index = len(self.data)
                self.data.append((key,value))
                self.all.add(index)
            self.index1.setdefault(key[0],set()).add(index)
            self.index2.setdefault(key[1],set()).add(index)
            self.index3.setdefault(key[2],set()).add(index)

    def __delitem__(self,key):
        indices,wild = self._indices(key)
        if not indices:
            raise KeyError
        self.index1[key[0]] -= indices
        self.index2[key[1]] -= indices
        self.index3[key[2]] -= indices
        self.all -= indices
        for i in indices:
            self.data[i] = None
        self.free.extend(indices)

    def __len__(self):
        return len(self.all)

    def __iter__(self):
        for key,value in self.data:
            yield key

例:

>>> db = JobDb()
>>> db['new jersey', 'mercer county', 'plumbers'] = 3
>>> db['new jersey', 'mercer county', 'programmers'] = 81
>>> db['new jersey', 'middlesex county', 'programmers'] = 81
>>> db['new jersey', 'middlesex county', 'salesmen'] = 62
>>> db['new york', 'queens county', 'plumbers'] = 9
>>> db['new york', 'queens county', 'salesmen'] = 36

>>> db['new york', None, None]
{('new york', 'queens county', 'plumbers'): 9,
 ('new york', 'queens county', 'salesmen'): 36}

>>> db[None, None, 'plumbers']
{('new jersey', 'mercer county', 'plumbers'): 3,
 ('new york', 'queens county', 'plumbers'): 9}

>>> db['new jersey', 'mercer county', None]
{('new jersey', 'mercer county', 'plumbers'): 3,
 ('new jersey', 'mercer county', 'programmers'): 81}

>>> db['new jersey', 'middlesex county', 'programmers']
81

>>>

編集:ワイルドカード(None)を使用してクエリを実行するときに辞書を返し、それ以外の場合は単一の値を返します。

1
Markus Jarderot

ラムダとdefaultdictで再帰を使用でき、名前を定義する必要はありません。

a = defaultdict((lambda f: f(f))(lambda g: lambda:defaultdict(g(g))))

以下に例を示します。

>>> a['new jersey']['mercer county']['plumbers']=3
>>> a['new jersey']['middlesex county']['programmers']=81
>>> a['new jersey']['mercer county']['programmers']=81
>>> a['new jersey']['middlesex county']['salesmen']=62
>>> a
defaultdict(<function __main__.<lambda>>,
        {'new jersey': defaultdict(<function __main__.<lambda>>,
                     {'mercer county': defaultdict(<function __main__.<lambda>>,
                                  {'plumbers': 3, 'programmers': 81}),
                      'middlesex county': defaultdict(<function __main__.<lambda>>,
                                  {'programmers': 81, 'salesmen': 62})})})
1
topkara

似たようなことがあります。私がやる多くの場合があります:

thedict = {}
for item in ('foo', 'bar', 'baz'):
  mydict = thedict.get(item, {})
  mydict = get_value_for(item)
  thedict[item] = mydict

しかし、多くのレベルに深く入ります。 「.get(item、{})」がキーになり、まだ辞書がない場合は別の辞書を作成します。その間、私はこれをより良く処理する方法を考えてきました。今、たくさんあります

value = mydict.get('foo', {}).get('bar', {}).get('baz', 0)

だから代わりに、私は作った:

def dictgetter(thedict, default, *args):
  totalargs = len(args)
  for i,arg in enumerate(args):
    if i+1 == totalargs:
      thedict = thedict.get(arg, default)
    else:
      thedict = thedict.get(arg, {})
  return thedict

あなたが行うと同じ効果があります:

value = dictgetter(mydict, 0, 'foo', 'bar', 'baz')

いい?私はそう思う。

0
uzi