Mam złożoną strukturę słownika, do której chciałbym uzyskać dostęp za pomocą listy kluczy, aby zaadresować właściwą pozycję.
dataDict = {
"a":{
"r": 1,
"s": 2,
"t": 3
},
"b":{
"u": 1,
"v": {
"x": 1,
"y": 2,
"z": 3
},
"w": 3
}
}
maplist = ["a", "r"]
lub
maplist = ["b", "v", "y"]
Stworzyłem następujący kod, który działa, ale jestem pewien, że jest lepszy i wydajniejszy sposób, aby to zrobić, jeśli ktoś ma pomysł.
# Get a given data from a dictionary with position provided as a list
def getFromDict(dataDict, mapList):
for k in mapList: dataDict = dataDict[k]
return dataDict
# Set a given data in a dictionary with position provided as a list
def setInDict(dataDict, mapList, value):
for k in mapList[:-1]: dataDict = dataDict[k]
dataDict[mapList[-1]] = value
python
list
dictionary
kolergy
źródło
źródło
Odpowiedzi:
Użyj,
reduce()
aby przejść przez słownik:i użyj ponownie,
getFromDict
aby znaleźć lokalizację do przechowywania wartościsetInDict()
:Wszystkie elementy oprócz ostatniego
mapList
są potrzebne do znalezienia słownika „nadrzędnego”, do którego zostanie dodana wartość, a następnie użyj ostatniego elementu, aby ustawić wartość na prawy klawisz.Próbny:
Zauważ, że przewodnik po stylu Python PEP8 określa nazwy snake_case dla funkcji . Powyższe działa równie dobrze w przypadku list lub kombinacji słowników i list, więc nazwy powinny być
get_by_path()
iset_by_path()
:źródło
try:
,except (KeyError, IndexError): return default_value
wokół aktualnejreturn
linii.dict.get()
zmienia semantykę, ponieważ zwraca,None
a nie podnosiKeyError
brakujące nazwy. Wszelkie kolejne nazwy wyzwalają następnieAttributeError
.operator
jest biblioteką standardową, nie ma potrzeby jej tutaj omijać.from functools import reduce
.for
pętli wydaje się bardziej pythonowe . Zobacz cytat z Co nowego w Pythonie 3.0 .KeyError
) - zobacz odpowiedź @ eafit na rozwiązanieDlaczego więc nie skorzystać z sugerowanej metody z pytania kolergy, aby uzyskać wartość:
I kod z odpowiedzi @ eafit na ustawienie wartości:
Oba działają bezpośrednio w Pythonie 2 i 3
źródło
getFromDict
, może zniszczyć osobę dzwoniącądataDict
. Ja bymcopy.deepcopy(dataDict)
pierwszy. Oczywiście (jak napisano) takie zachowanie jest pożądane w drugiej funkcji.Korzystanie z funkcji Redukcja jest sprytne, ale metoda set OP może powodować problemy, jeśli klucze nadrzędne nie istnieją wcześniej w zagnieżdżonym słowniku. Ponieważ jest to pierwszy wpis SO, jaki widziałem na ten temat w mojej wyszukiwarce Google, chciałbym go nieco ulepszyć.
Metoda set w ( Ustawianie wartości w zagnieżdżonym słowniku języka Python z uwzględnieniem listy indeksów i wartości ) wydaje się bardziej odporna na brak kluczy rodzicielskich. Aby go skopiować:
Wygodne może być również posiadanie metody, która przechodzi przez drzewo kluczy i uzyskuje wszystkie bezwzględne ścieżki kluczy, dla których utworzyłem:
Jednym z jej zastosowań jest konwersja zagnieżdżonego drzewa do pandy DataFrame przy użyciu następującego kodu (przy założeniu, że wszystkie liście w zagnieżdżonym słowniku mają tę samą głębokość).
źródło
nested_set
?Ta biblioteka może być pomocna: https://github.com/akesterson/dpath-python
źródło
A co z używaniem funkcji rekurencyjnych?
Aby uzyskać wartość:
I ustawić wartość:
źródło
Czysty styl Pythona, bez importu:
Wynik
źródło
Alternatywny sposób, jeśli nie chcesz zgłaszać błędów, jeśli nie ma jednego z kluczy (aby twój główny kod mógł działać bez przerwy):
W takim przypadku, jeśli którykolwiek z kluczy wejściowych nie jest obecny, zwracany jest None, którego można użyć jako sprawdzenia w kodzie głównym w celu wykonania alternatywnego zadania.
źródło
Zamiast sprawdzać wydajność za każdym razem, gdy chcesz wyszukać wartość, możesz raz spłaszczyć słownik, a następnie po prostu wyszukać klucz, taki jak
b:v:y
W ten sposób możesz po prostu wyszukać przedmioty za pomocą,
flat_dict['b:v:y']
które Ci podadzą1
.Zamiast przechodzenia przez słownik przy każdym wyszukiwaniu, możesz to przyspieszyć, spłaszczając słownik i zapisując dane wyjściowe, tak aby wyszukiwanie z zimnego startu oznaczało załadowanie spłaszczonego słownika i po prostu wykonanie wyszukiwania klucza / wartości bez przechodzenie.
źródło
Rozwiązałem to za pomocą rekurencji:
Na podstawie twojego przykładu:
źródło
Co powiesz na sprawdzenie, a następnie ustawienie elementu dict bez dwukrotnego przetwarzania wszystkich indeksów?
Rozwiązanie:
Przykładowy przepływ pracy:
Test
źródło
Bardzo późno na imprezę, ale wysłanie posta na wypadek, gdyby to mogło komuś pomóc w przyszłości. W moim przypadku następująca funkcja działała najlepiej. Działa w celu wyciągnięcia dowolnego typu danych ze słownika
dict to słownik zawierający naszą wartość
lista to lista „kroków” w kierunku naszej wartości
źródło
Z satysfakcją można zobaczyć te odpowiedzi w przypadku posiadania dwóch statycznych metod ustawiania i uzyskiwania zagnieżdżonych atrybutów. Te rozwiązania są o wiele lepsze niż używanie zagnieżdżonych drzew https://gist.github.com/hrldcpr/2012250
Oto moja realizacja.
Zastosowanie :
Aby ustawić zagnieżdżone wywołanie atrybutu
sattr(my_dict, 1, 2, 3, 5) is equal to my_dict[1][2][3][4]=5
Aby uzyskać zagnieżdżone wywołanie atrybutu
gattr(my_dict, 1, 2)
źródło
Proponuję użyć
python-benedict
do uzyskania dostępu do zagnieżdżonych elementów za pomocą keypath.Zainstaluj go za pomocą
pip
:Następnie:
Tutaj pełna dokumentacja: https://github.com/fabiocaccamo/python-benedict
źródło
Jeśli chcesz również mieć możliwość pracy z dowolnym jsonem, w tym zagnieżdżonymi listami i dyktami, oraz ładnie obsługiwać nieprawidłowe ścieżki wyszukiwania, oto moje rozwiązanie:
źródło
metoda łączenia ciągów:
źródło
Rozszerzając podejście @DomTomCat i innych, te funkcjonalne (tj. Zwracają zmodyfikowane dane przez głębokie kopiowanie bez wpływu na dane wejściowe) setter i mapper działają dla zagnieżdżonych
dict
ilist
.seter:
mapper:
źródło
Możesz skorzystać z tej
eval
funkcji w Pythonie.Wyjaśnienie
Przykładowe zapytanie:
maplist = ["b", "v", "y"]
nestq
będzie"nest['b']['v']['y']"
gdzienest
jest zagnieżdżony słownik.Funkcja
eval
wbudowana wykonuje podany ciąg. Należy jednak uważać na możliwe luki w zabezpieczeniach, które wynikają z używaniaeval
funkcji. Dyskusję można znaleźć tutaj:W
nested_parse()
funkcji upewniłem się, że żadne__builtins__
zmienne globalne nie są dostępne, anest
słownik jest jedyną dostępną zmienną lokalną .źródło
Możesz użyć pydash:
https://pydash.readthedocs.io/en/latest/api.html
źródło