Uważam, że wygodniej jest uzyskiwać dostęp do klawiszy dykta obj.foo
zamiast obj['foo']
, więc napisałem ten fragment:
class AttributeDict(dict):
def __getattr__(self, attr):
return self[attr]
def __setattr__(self, attr, value):
self[attr] = value
Zakładam jednak, że musi istnieć jakiś powód, dla którego Python nie zapewnia tej funkcjonalności po wyjęciu z pudełka. Jakie byłyby ograniczenia i pułapki związane z dostępem do kluczy dict w ten sposób?
python
dictionary
syntax
attributes
Izz ad-Din Ruhulessin
źródło
źródło
collections.namedtuple
jest do tego bardzo przydatny.easydict.EasyDict
Odpowiedzi:
Najlepszym sposobem na to jest:
Niektóre zalety:
.keys()
. Działają dobrze. Chyba że - oczywiście - przypisujesz im jakąś wartość, patrz poniżej)AttributeError
zamiastKeyError
Cons:
.keys()
będą działać dobrze, jeśli zostaną nadpisane przez przychodzące daneE1123(unexpected-keyword-arg)
iE1103(maybe-no-member)
Krótkie wyjaśnienie, jak to działa
__dict__
.__dict__
musiał być „zwykłym słownikiem ”, więc możemy przypisać dowolną podklasędict()
do słownika wewnętrznego.AttrDict()
instancję, którą tworzymy (jak jesteśmy__init__
).super()
„s__init__()
sposób możemy upewnić się, że to (już) zachowuje się dokładnie tak samo jak w słowniku, ponieważ funkcja zwraca cały słownik instancji kod.Jednym z powodów, dla których Python nie zapewnia tej funkcjonalności po wyjęciu z pudełka
Jak zauważono na liście „wad”, łączy to przestrzeń nazw przechowywanych kluczy (które mogą pochodzić z dowolnych i / lub niezaufanych danych!) Z przestrzenią nazw wbudowanych atrybutów metody dict. Na przykład:
źródło
.
, nie możesz łamać reguł języka :) I nie chciałbymAttrDict
automatycznie przekształcać pól zawierających spacje w coś innego.Jeśli używasz notacji tablicowej, możesz mieć wszystkie legalne ciągi znaków jako część klucza. Na przykład,
obj['!#$%^&*()_']
źródło
What would be the caveats and pitfalls of accessing dict keys in this manner?
(jako atrybuty), a odpowiedź brzmi: większość przedstawionych tutaj znaków nie będzie użyteczna.Z tego drugiego pytania SO jest świetny przykład implementacji, który upraszcza twój istniejący kod. Co powiesz na:
Znacznie bardziej zwięzły i nie pozostawia miejsca na dodatkowe cruft dostające się do ciebie
__getattr__
i__setattr__
funkcjonuje w przyszłości.źródło
d = AttributeDict(foo=1)
.d.bar = 1
atrybut bar jest przechowywany w atrybucie dict, ale nie w samym dict. drukowanied
pokazuje tylko element foo.d = AttributeDict(foo=1);d.bar = 1;print d
=>{'foo': 1, 'bar': 1}
Działa dla mnie!__getattr__
metodę, która podnosi,AttributeError
jeśli dany atrybut nie istnieje, w przeciwnym razie rzeczy takie jakgetattr(obj, attr, default_value)
nie działają (tzn. Nie zwracają się,default_value
jeśliattr
nie istniejąobj
)Gdzie odpowiadam na zadane pytanie
Dlaczego Python nie oferuje tego po wyjęciu z pudełka?
Podejrzewam, że ma to związek z Zen Pythona : „Powinien istnieć jeden - a najlepiej tylko jeden - oczywisty sposób”. Stworzyłoby to dwa oczywiste sposoby dostępu do wartości ze słowników:
obj['key']
iobj.key
.Ostrzeżenia i pułapki
Należą do nich możliwy brak jasności i zamieszanie w kodzie. to znaczy, że poniższe informacje mogą być mylące dla kogoś , kto zamierza zachować kod w późniejszym terminie, a nawet dla ciebie, jeśli nie wrócisz do niego przez jakiś czas. Znów z Zen : „Czytelność się liczy!”
Jeśli
d
zostanie utworzona instancja lubKEY
zostanie zdefiniowana lubd[KEY]
zostanie przypisana z dala od miejsca, w którymd.spam
jest używana, może łatwo prowadzić do nieporozumień co do tego, co się dzieje, ponieważ nie jest to często używany idiom. Wiem, że mogłoby to mnie pomylić.Dodatkowo, jeśli zmienisz wartość w
KEY
następujący sposób (ale przegapisz zmianęd.spam
), otrzymasz teraz:IMO, nie warte wysiłku.
Inne przedmioty
Jak zauważyli inni, możesz użyć dowolnego obiektu haszującego (nie tylko łańcucha) jako klucza dyktafonu. Na przykład,
jest legalne, ale
nie jest. Daje to dostęp do całego zakresu drukowalnych znaków lub innych obiektów możliwych do skrótu dla kluczy słownika, których nie masz podczas uzyskiwania dostępu do atrybutu obiektu. Umożliwia to taką magię jak metaklasa obiektu w pamięci podręcznej, jak przepis z Python Cookbook (rozdz. 9) .
W którym redaguję
Wolę estetykę
spam.eggs
ponadspam['eggs']
(myślę, że wygląda na czystszą) i naprawdę zacząłem pragnąć tej funkcjonalności, kiedy ją spotkałemnamedtuple
. Ale wygoda bycia w stanie wykonać następujące atuty.Jest to prosty przykład, ale często używam dyktatów w różnych sytuacjach niż w
obj.key
notacji (tj. Kiedy muszę czytać prefiksy z pliku XML). W innych przypadkach, gdy kusi mnie tworzenie instancji klasy dynamicznej i klepanie w nią niektórych atrybutów ze względów estetycznych, nadal używam dykta dla zachowania spójności w celu zwiększenia czytelności.Jestem pewien, że OP już dawno to rozwiązał, ale jeśli nadal chce tę funkcjonalność, sugeruję pobranie jednego z pakietów, które ją udostępnia:
Wiązka jest tą, którą znam bardziej. Podklasadict
, więc masz całą tę funkcjonalność.AttrDict również wygląda na całkiem niezły, ale nie znam go tak dobrze i nie przeglądałem źródła tak szczegółowo, jak mam Bunch .Jednak w celu poprawy czytelności jego kodu zdecydowanie zalecam, aby nie mieszał swoich stylów notacji. Jeśli woli ten zapis, powinien po prostu utworzyć obiekt dynamiczny, dodać do niego pożądane atrybuty i nazwać go dniem:
W czym aktualizuję, aby odpowiedzieć na pytanie uzupełniające w komentarzach
W komentarzach (poniżej) Elmo pyta:
Chociaż nigdy nie użyłem tego przypadku użycia (ponownie, zwykle
dict
dla zachowania spójności używam zagnieżdżenia ), działa następujący kod:źródło
Zastrzeżenie emptor: Z niektórych powodów takie klasy wydają się zepsuć pakiet wieloprocesowy. Przed chwilą zmagałem się z tym błędem, zanim znalazłem SO: Znalezienie wyjątku w wieloprocesowym przetwarzaniu Pythona
źródło
Możesz pobrać wygodną klasę kontenera ze standardowej biblioteki:
aby uniknąć konieczności kopiowania bitów kodu. Brak standardowego dostępu do słownika, ale łatwo go odzyskać, jeśli naprawdę chcesz. Kod w argparse jest prosty,
źródło
types.SimpleNamespace
docs.python.org/dev/library/types.html#types.SimpleNamespaceCo jeśli chciałbyś klucza, który był metodą, taką jak
__eq__
lub__getattr__
?I nie byłbyś w stanie mieć wpisu, który nie zaczynał się na literę, więc używanie
0343853
jako klucza jest niemożliwe .A co jeśli nie chcesz używać łańcucha?
źródło
pickle.dump
wykorzystuje__getstate__
krotek można użyć klawiszy dict. Jak uzyskasz dostęp do krotki w swojej konstrukcji?
Ponadto namedtuple jest wygodną strukturą, która może dostarczać wartości poprzez dostęp do atrybutów.
źródło
Co powiesz na Prodict , małą klasę Python, którą napisałem, by rządzić nimi wszystkimi :)
Dodatkowo otrzymujesz automatyczne uzupełnianie kodu , rekurencyjne tworzenie instancji obiektów i automatyczną konwersję typów !
Możesz zrobić dokładnie to, o co prosiłeś:
Przykład 1: Podpowiedź do tekstu
Przykład 2: Automatyczna konwersja typu
źródło
Nie działa ogólnie. Nie wszystkie prawidłowe klucze dict tworzą atrybuty adresowalne („klucz”). Musisz więc uważać.
Obiekty Pythona to w zasadzie słowniki. Wątpię więc, czy jest dużo wyników lub innych kar.
źródło
To nie odnosi się do pierwotnego pytania, ale powinno być przydatne dla osób, które, jak ja, kończą tutaj, szukając biblioteki zapewniającej tę funkcjonalność.
Addict to świetna biblioteka do tego: https://github.com/mewwts/addict rozwiązuje wiele problemów wymienionych w poprzednich odpowiedziach.
Przykład z dokumentów:
Z uzależnionym:
źródło
Zastanawiałem się, jaki jest obecny stan „kluczy dyktujących jako attr” w ekosystemie Pythona. Jak zauważyło kilku komentujących, prawdopodobnie nie jest to coś, co chcesz rzucić od zera , ponieważ istnieje kilka pułapek i pistoletów, niektóre z nich bardzo subtelne. Poza tym nie polecałbym używania
Namespace
jako klasy podstawowej, byłem na tej drodze, to nie jest ładne.Na szczęście istnieje kilka pakietów open source zapewniających tę funkcjonalność, gotowych do instalacji w trybie pip! Niestety istnieje kilka pakietów. Oto streszczenie z grudnia 2019 r.
Uczestnicy (najnowsze zobowiązania do opanowania | #commits | # contribs | pokrycie%):
Nieobsługiwany lub niedostatecznie konserwowany:
Obecnie polecam muncha lub uzależnia . Mają najwięcej zatwierdzeń, autorów i wydań, sugerując dla nich zdrową bazę kodu open source. Mają najczystszy readme.md, 100% zasięgu i dobrze wyglądający zestaw testów.
Nie mam psa w tym wyścigu (na razie!), Poza tym, że stworzyłem własny kod dict / attr i zmarnowałem mnóstwo czasu, ponieważ nie znałem wszystkich tych opcji :). Mogę przyczynić się do uzależnienia / chrupania w przyszłości, ponieważ wolałbym zobaczyć jedną solidną paczkę niż kilka rozdrobnionych. Jeśli ci się podobają, wnieś swój wkład! W szczególności wygląda na to, że Munch mógłby użyć znaczka codecov, a uzależniony może użyć znaczka wersji Python.
uzależnieni profesjonaliści:
uzależniony od:
typing.Dict
jeśli tyfrom addict import Dict
munch plusy:
Munch Cons:
W którym redaguję
Wiele księżyców temu, kiedy korzystałem z edytorów tekstu do pisania Pythona, w projektach z samym sobą lub innym twórcą, spodobał mi się styl dict-attrs, możliwość wstawiania kluczy po prostu zadeklarując
foo.bar.spam = eggs
. Teraz pracuję w zespołach i do wszystkiego używam IDE. Odszedłem od tego rodzaju struktur danych i ogólnie dynamicznego pisania na rzecz analizy statycznej, technik funkcjonalnych i wskazówek dotyczących typów. Zacząłem eksperymentować z tą techniką, dzieląc Pstruct na obiekty mojego własnego projektu:Daje to obiekt, który nadal zachowuje się jak dyktando, ale umożliwia także dostęp do kluczy takich jak atrybuty, w znacznie bardziej sztywny sposób. Zaletą jest to, że ja (lub nieszczęsni konsumenci twojego kodu) dokładnie wiem, jakie pola mogą istnieć, a jakie nie, a IDE może automatycznie uzupełniać pola. Również podklasowanie wanilii
dict
oznacza, że serializacja Json jest łatwa. Myślę, że następną ewolucją tego pomysłu byłby niestandardowy generator protobufów, który emituje te interfejsy, a miłą konsekwencją jest uzyskanie międzygranicznych struktur danych i IPC za pośrednictwem gRPC za darmo.Jeśli zdecydujesz się na dyktowanie, ważne jest, aby udokumentować, jakie pola są oczekiwane, dla własnego zdrowia psychicznego (i członków drużyny).
Edytuj / aktualizuj ten post, aby był aktualny!
źródło
addict
jest to, że nie podniesie wyjątków, gdy źleDict
przeliterujesz atrybut, ponieważ zwróci nowy (jest to konieczne, aby foo.abc = „bar” działał).Oto krótki przykład niezmiennych zapisów wykorzystujących wbudowane
collections.namedtuple
:i przykład użycia:
źródło
Aby urozmaicić odpowiedź, sci-kit learn zaimplementował to jako
Bunch
:Wszystko, czego potrzebujesz, to zdobyć metody
setattr
igetattr
-getattr
sprawdzenie kluczy dyktafów i przejście do sprawdzania rzeczywistych atrybutów. Jestsetstaet
to poprawka do usuwania / paczkowania „pęczków” - w razie zainteresowania sprawdź https://github.com/scikit-learn/scikit-learn/issues/6196źródło
Nie trzeba pisać własnego, ponieważ setattr () i getattr () już istnieją.
Przewaga obiektów klasowych prawdopodobnie odgrywa rolę w definiowaniu i dziedziczeniu klas.
źródło
Stworzyłem to na podstawie danych wejściowych z tego wątku. Muszę jednak użyć odict, więc musiałem zastąpić get i ustawić attr. Myślę, że powinno to działać w przypadku większości zastosowań specjalnych.
Sposób użycia wygląda następująco:
Klasa:
Jest to całkiem fajny wzorzec już wspomniany w wątku, ale jeśli chcesz tylko zrobić dykt i przekonwertować go na obiekt, który działa z funkcją autouzupełniania w środowisku IDE itp .:
źródło
Najwyraźniej istnieje teraz biblioteka do tego - https://pypi.python.org/pypi/attrdict - która implementuje tę dokładną funkcjonalność oraz scalanie rekurencyjne i ładowanie json. Może warto rzucić na to okiem.
źródło
Tego używam
źródło
namedtuple('Args', list(args.keys()))(**args)
Możesz to zrobić, korzystając z klasy, którą właśnie stworzyłem. Dzięki tej klasie możesz używać
Map
obiektu jako innego słownika (w tym serializacji json) lub z notacją kropkową. Mam nadzieję, że ci pomogę:Przykłady użycia:
źródło
dict
metody, np .:m=Map(); m["keys"] = 42; m.keys()
dajeTypeError: 'int' object is not callable
.field/attribute
a nie amethod
, ale jeśli przypiszesz metodę zamiast liczby, możesz uzyskać do niej dostępm.method()
.Pozwól, że opublikuję kolejną implementację, która opiera się na odpowiedzi Kinvais, ale integruje pomysły z AttributeDict zaproponowane w http://databio.org/posts/python_AttributeDict.html .
Zaletą tej wersji jest to, że działa ona również dla zagnieżdżonych słowników:
źródło
źródło
Rozwiązaniem jest:
źródło
Jak sugeruje @Henry, jednym z powodów, dla których dostęp kropkowany nie może być używany w dyktach, jest to, że ogranicza nazwy kluczy dykt do zmiennych poprawnych w języku python, ograniczając w ten sposób wszystkie możliwe nazwy.
Poniżej podano przykłady, dlaczego dostęp punktowy nie byłby ogólnie pomocny, biorąc pod uwagę dyktando,
d
:Ważność
Następujące atrybuty byłyby nieprawidłowe w Pythonie:
Styl
Konwencje PEP8 nakładałyby miękkie ograniczenie na nazewnictwo atrybutów:
A. Zarezerwowane nazwy słów kluczowych (lub funkcji wbudowanych):
B. Reguła dotycząca nazw metod i zmiennych :
Czasami te obawy pojawiają się w bibliotekach takich jak pandy , które umożliwiają kropkowany dostęp do kolumn DataFrame według nazw. Domyślnym mechanizmem rozwiązywania ograniczeń nazewnictwa jest także notacja tablicowa - ciąg znaków w nawiasach.
Jeśli te ograniczenia nie dotyczą twojego przypadku użycia, istnieje kilka opcji dotyczących struktur danych z dostępem punktowym .
źródło
Możesz użyć dict_to_obj https://pypi.org/project/dict-to-obj/ Robi dokładnie to, o co prosiłeś
źródło
.idea
i plików specyficznych dla użytkownika lub wygenerowanych przez IDE.gitignore
.To nie jest „dobra” odpowiedź, ale pomyślałem, że to fajne (nie obsługuje zagnieżdżonych dykt w obecnej formie). Po prostu zawiń swój dykt w funkcję:
Teraz masz nieco inną składnię. Aby uzyskać dostęp do elementów nagrania tak, jak robią to atrybuty
f.key
. Aby uzyskać dostęp do elementów dict (i innych metod dict) w zwykły sposób,f()['key']
możemy i wygodnie zaktualizować dict, wywołując f z argumentami słów kluczowych i / lub słownikiemPrzykład
I oto jest. Będę szczęśliwy, jeśli ktoś zasugeruje zalety i wady tej metody.
źródło
Jak zauważył Doug, istnieje pakiet Bunch, którego można użyć do uzyskania
obj.key
funkcjonalności. W rzeczywistości istnieje nowsza wersja o nazwieNeoBunch
Ma jednak świetną funkcję konwersji słownika do obiektu NeoBunch za pomocą funkcji neobunchify . Często używam szablonów Mako i przekazywanie danych, ponieważ obiekty NeoBunch sprawiają, że są one znacznie bardziej czytelne, więc jeśli zdarzy się, że użyjesz normalnego dykta w swoim programie Python, ale chcesz notacji kropkowej w szablonie Mako, możesz użyć tego w ten sposób:
Szablon Mako mógłby wyglądać następująco:
źródło
Najłatwiej jest zdefiniować klasę, nazwijmy ją Przestrzeń nazw. , który korzysta z obiektu dict .update () na dykcie. Następnie dykt będzie traktowany jak obiekt.
źródło