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']
i obj.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!”
>>> KEY = 'spam'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Jeśli d
zostanie utworzona instancja lub KEY
zostanie zdefiniowana lub d[KEY]
zostanie przypisana z dala od miejsca, w którym d.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:
>>> KEY = 'foo'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: 'C' object has no attribute 'spam'
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,
>>> d = {(2, 3): True,}
>>> assert d[(2, 3)] is True
>>>
jest legalne, ale
>>> C = type('C', (object,), {(2, 3): True})
>>> d = C()
>>> assert d.(2, 3) is True
File "<stdin>", line 1
d.(2, 3)
^
SyntaxError: invalid syntax
>>> getattr(d, (2, 3))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: getattr(): attribute name must be string
>>>
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
ponad spam['eggs']
(myślę, że wygląda na czystszą) i naprawdę zacząłem pragnąć tej funkcjonalności, kiedy ją spotkałem namedtuple
. Ale wygoda bycia w stanie wykonać następujące atuty.
>>> KEYS = 'spam eggs ham'
>>> VALS = [1, 2, 3]
>>> d = {k: v for k, v in zip(KEYS.split(' '), VALS)}
>>> assert d == {'spam': 1, 'eggs': 2, 'ham': 3}
>>>
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 .
- Addict jest aktywnie utrzymywany i zapewnia dostęp podobny do attr i więcej.
- Jak zauważono w komentarzach Rotaretiego, Bunch jest przestarzały, ale istnieje aktywny widelec o nazwie Munch .
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:
>>> C = type('C', (object,), {})
>>> d = C()
>>> d.spam = 1
>>> d.eggs = 2
>>> d.ham = 3
>>> assert d.__dict__ == {'spam': 1, 'eggs': 2, 'ham': 3}
W czym aktualizuję, aby odpowiedzieć na pytanie uzupełniające w komentarzach
W komentarzach (poniżej) Elmo pyta:
Co jeśli chcesz zejść głębiej? (w odniesieniu do typu (...))
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:
>>> C = type('C', (object,), {})
>>> d = C()
>>> for x in 'spam eggs ham'.split():
... setattr(d, x, C())
... i = 1
... for y in 'one two three'.split():
... setattr(getattr(d, x), y, i)
... i += 1
...
>>> assert d.spam.__dict__ == {'one': 1, 'two': 2, 'three': 3}
collections.namedtuple
jest do tego bardzo przydatny.