중첩된 사전을 구현하는 가장 좋은 방법은 무엇입니까?
저는 기본적으로 중첩된 사전과 같은 데이터 구조를 가지고 있습니다.예를 들어 다음과 같습니다.
{'new jersey': {'mercer county': {'plumbers': 3,
'programmers': 81},
'middlesex county': {'programmers': 81,
'salesmen': 62}},
'new york': {'queens county': {'plumbers': 9,
'salesmen': 36}}}
이것을 유지·작성하는 것은 매우 번거로운 일입니다.새로운 주/군/전문직이 생길 때마다 불쾌한 시도/캐치 블록을 통해 하층 사전을 작성해야 합니다.또한 모든 값을 검토하려면 성가신 중첩 반복기를 생성해야 합니다.
다음과 같이 튜플을 키로 사용할 수도 있습니다.
{('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()
수동으로 설정합니다.
Python에서 중첩된 사전을 구현하는 가장 좋은 방법은 무엇입니까?
건건 、 않은 、 ,지마마마마 。하고, 「예상 사전」을 사용합니다.dict.setdefault
즉되는 apropos가 됩니다.KeyError
만약 당신이 이 행동을 고집한다면, 당신의 발을 쏘는 방법은 다음과 같습니다.
__missing__
dict
새 instance.subclass를 합니다.
이 접근법은 Python 2.5부터 사용 가능(그리고 문서화되어 있음)했으며 (특히 나에게 가치 있는) autovivated defaultdict의 추악한 인쇄 대신 일반 dict와 같이 예쁘게 인쇄됩니다.
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', {})
또 다른 비판은 setdefault를 사용하든 사용하지 않든 새로운 인스턴스가 필요하다는 것입니다.그러나 Python(또는 적어도 CPython)은 사용되지 않거나 참조되지 않은 새 인스턴스를 처리하는 데 다소 스마트합니다. 예를 들어, 메모리 내의 위치를 재사용합니다.
>>> id({}), id({}), id({})
(523575344, 523575344, 523575344)
auto-vivified 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>, {})})})
이 출력은 매우 부적절하고 결과를 읽을 수 없습니다.일반적으로 해결책은 수동 검사를 위해 다시 dict로 재귀적으로 변환하는 것입니다.이 간단한 해법은 독자들에게 연습으로 남겨져 있다.
성능
마지막으로 퍼포먼스를 살펴보겠습니다.인스턴스화 비용을 빼고 있습니다.
>>> 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 노트북 등)에 필요한 경우 퍼포먼스는 그다지 중요하지 않습니다.비비딕트 객체을 사용)와합니다.__getitem__
__missing__
(그것은 이 목적을 위해 만들어졌다) 훨씬 우수하다.
결론
★★의 __missing__
분류로dict
, 그 장점이 .
- 간단한 인스턴스화
- 간단한 데이터 입력
- 간단한 데이터 표시
수정하는 보다 덜 때문입니다.__getitem__
보다 더
단, 다음과 같은 단점이 있습니다.
- 잘못된 검색은 자동으로 실패합니다.
- 잘못된 조회는 사전에 남아 있습니다.
그래서 저는 개인적으로setdefault
그리고 내가 이런 행동을 필요로 했던 모든 상황에서.
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}}}
이렇게 작은 것을 본 적이 없다고 해서, 여기 당신이 원하는 만큼 네스트 할 수 있는 딕트가 있습니다.
# yo dawg, i heard you liked dicts
def yodict():
return defaultdict(yodict)
PyYaml을 사용하여 YAML 파일을 생성하여 에서 읽을 수 있습니다.
순서 1: YAML 파일 「employment.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(...)
.
스타 스키마 설계가 있으므로 사전보다는 관계형 표와 같은 구조로 구성할 수 있습니다.
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 오버헤드 없이 데이터 웨어하우스와 같은 설계를 구축하는 데 큰 도움이 됩니다.
는, 「네스트 레벨」을 합니다.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()
등등.
임의 깊이의 중첩된 사전을 반환하는 함수입니다.
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
나중에 새 항목을 dict에 추가할 수 없도록 할 수도 있습니다. 모든 은 쉽습니다.defaultdict
normal s(표준)dict
s.
def dictify(d):
for k,v in d.iteritems():
if isinstance(v,defaultdict):
d[k] = dictify(v)
return dict(d)
다른 사람들이 제안했듯이 관계형 데이터베이스가 더 유용할 수 있습니다.메모리 내 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"'))
이것은 단순한 예에 불과합니다.주, 카운티 및 직급에 대해 별도의 테이블을 정의할 수 있습니다.
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)
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)
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}}}}}
"불유쾌한 시도/잡기 블록"에 대해서는:
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
defaultdict()
★★★★★★★★★★★★★★★★!
2차원 사전의 경우 다음을 수행할 수 있습니다.
d = defaultdict(defaultdict)
d[1][2] = 3
더 많은 치수를 위해 다음을 수행할 수 있습니다.
d = defaultdict(lambda :defaultdict(defaultdict))
d[1][2][3] = 4
중첩된 사전을 쉽게 반복할 수 있도록 간단한 생성기를 작성하면 어떨까요?
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 blocks를 사용합니까?키를 검색하기 전에 dict에 키가 존재하는지 여부를 문의하는 것이 매우 쉽고 안전할 수 있습니다.guard 구를 사용하는 함수는 다음과 같습니다.
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)
그러나 좀 더 간결한 방법으로 python 2.5 이후 표준 라이브러리의 일부인 collections.defaultdict를 사용하는 것을 검토해 보십시오.
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']
여기서는 데이터 구조의 의미를 가정하고 있지만 실제로 하고 싶은 일에 맞게 조정하기가 쉬울 것입니다.
.__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>
하지만 대부분 그런 것을 구현하면 정말 재밌을 것 같아요.
데이터셋이 매우 작지 않다면 관계형 데이터베이스를 사용하는 것을 고려해 보는 것이 좋습니다.카운트를 쉽게 추가하거나 카운트의 서브셋을 선택하거나 주, 카운티, 직업 또는 이들의 조합별로 집계 카운트를 선택할 수 있습니다.
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
를 참조해 주세요.
저도 비슷한 일이 있어요.다음과 같은 경우가 많이 있습니다.
thedict = {}
for item in ('foo', 'bar', 'baz'):
mydict = thedict.get(item, {})
mydict = get_value_for(item)
thedict[item] = mydict
하지만 여러 단계로 깊이 들어가죠..get(항목, {})이(가) 열쇠입니다.사전이 없으면 다른 사전을 만들 수 있기 때문입니다.그동안, 저는 이 문제를 더 잘 해결할 방법을 생각해왔습니다.지금 이 순간에도
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')
좀 나아요? 그런 것 같아요.
이름을 정의할 필요 없이 lamdas 및 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})})})
저는 이 기능을 사용했어요.안전하고, 빠르고, 유지보수가 용이합니다.
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
>>>
다음에 대해서는 (위에서 복사한) 추가 기능을 구현하는 방법이 있습니다.중첩된 사전을 사용하여 값을 배열로 저장하려고 합니다.
class Vividict(dict):
def __missing__(self, key):
value = self[key] = type(self)() # retain local pointer to value
return value
현재 구현은 다음과 같습니다.
totalGeneHash=Vividict()
for keys in GenHash:
for second in GenHash[keys]:
if keys in sampleHash:
total_val = GenHash[keys][second]
totalGeneHash[gene][keys].append(total_val)
This is the error I get: AttributeError: 'Vividict' object has no attribute 'append'
언급URL : https://stackoverflow.com/questions/635483/what-is-the-best-way-to-implement-nested-dictionaries
'programing' 카테고리의 다른 글
Vue.js와 Flask를 결합하려면 어떻게 해야 하나요? (0) | 2023.01.23 |
---|---|
Python에서 여러 줄의 코멘트를 작성하려면 어떻게 해야 하나요? (0) | 2023.01.13 |
핸들바.js {{#if}} 조건부 논리 연산자 (0) | 2023.01.13 |
현재 날짜의 타임스탬프가 있는 행을 선택하려면 어떻게 해야 합니까? (0) | 2023.01.13 |
경고/오류 "함수 선언은 프로토타입이 아닙니다" (0) | 2023.01.13 |