Istnieje co najmniej pięć sześć sposobów. Preferowany sposób zależy od twojego przypadku użycia.
Opcja 1: Po prostu dodaj asdict()metodę.
Opierając się na opisie problemu, bardzo bym się zastanowił nad asdictsposobem postępowania sugerowanym przez inne odpowiedzi. Dzieje się tak, ponieważ nie wygląda na to, że Twój obiekt jest naprawdę dużą kolekcją:
class Wharrgarbl(object):
...
def asdict(self):
return {'a': self.a, 'b': self.b, 'c': self.c}
Korzystanie z innych opcji poniżej może być mylące dla innych, chyba że jest bardzo oczywiste, które elementy składowe obiektu będą, a które nie byłyby iterowane lub określone jako pary klucz-wartość.
Opcja 1a: Dziedzicz swoją klasę z 'typing.NamedTuple'(lub w większości równoważną 'collections.namedtuple') i użyj dostarczonej _asdictmetody .
from typing import NamedTuple
class Wharrgarbl(NamedTuple):
a: str
b: str
c: str
sum: int = 6
version: str = 'old'
Korzystanie z imieniem krotki to bardzo wygodny sposób, aby dodać wiele funkcjonalności do swojej klasie przy minimum wysiłku, w tym w _asdictmetodzie . Jednak ograniczenie polega na tym, że, jak pokazano powyżej, NT będzie obejmował wszystkich członków _asdict.
Jeśli są członkowie, których nie chcesz uwzględniać w swoim słowniku, musisz zmodyfikować _asdictwynik:
from typing import NamedTuple
class Wharrgarbl(NamedTuple):
a: str
b: str
c: str
sum: int = 6
version: str = 'old'
def _asdict(self):
d = super()._asdict()
del d['sum']
del d['version']
return d
Innym ograniczeniem jest to, że NT jest tylko do odczytu. Może to być pożądane lub nie.
Opcja 2: Wdrożenie __iter__.
W ten sposób na przykład:
def __iter__(self):
yield 'a', self.a
yield 'b', self.b
yield 'c', self.c
Teraz możesz po prostu:
dict(my_object)
To działa, ponieważ dict()konstruktor akceptuje iterowalne (key, value)pary w celu skonstruowania słownika. Zanim to zrobisz, zadaj sobie pytanie, czy iterowanie obiektu jako szeregu par kluczy i wartości w ten sposób - choć wygodne do tworzenia dict- może w rzeczywistości być zaskakującym zachowaniem w innych kontekstach. Np. Zadaj sobie pytanie „jakie powinno być zachowanie list(my_object)…?”
Ponadto należy pamiętać, że dostęp do wartości bezpośrednio przy użyciu obj["a"]składni get item nie będzie działać, a rozpakowywanie argumentów słów kluczowych nie będzie działać. W takim przypadku musisz zaimplementować protokół mapowania.
Wariant 3: Realizacja z protokołem odwzorowania . Pozwala to na zachowanie dostępu według klucza, rzutowanie na a dictbez użycia __iter__, a także zapewnia zachowanie podczas rozpakowywania ( {**my_obj}) i rozpakowywanie słów kluczowych, jeśli wszystkie klucze są ciągami ( dict(**my_obj)).
Protokół mapowania wymaga podania (co najmniej) dwóch metod jednocześnie: keys()i __getitem__.
class MyKwargUnpackable:
def keys(self):
return list("abc")
def __getitem__(self, key):
return dict(zip("abc", "one two three".split()))[key]
Teraz możesz:
>>> m=MyKwargUnpackable()
>>> m["a"]
'one'
>>> dict(m)
{'a': 'one', 'b': 'two', 'c': 'three'}
>>> dict(**m)
{'a': 'one', 'b': 'two', 'c': 'three'}
Jak wspomniano powyżej, jeśli używasz wystarczająco nowej wersji Pythona, możesz również rozpakować swój obiekt mapping-protocol do wyrażenia słownikowego, takiego jak ten (iw tym przypadku nie jest wymagane, aby twoje klucze były ciągami):
>>> {**m}
{'a': 'one', 'b': 'two', 'c': 'three'}
Należy zauważyć, że protokół mapowanie ma pierwszeństwo w __iter__sposób podczas odlewania przedmiotu do dictbezpośrednio (bez użycia kwarg rozpakowaniu tj dict(m)). Jest więc możliwe - i czasami wygodne - spowodowanie, aby obiekt zachowywał się inaczej, gdy jest używany jako iterowalny (np. list(m)), A gdy jest rzutowany na a dict( dict(m)).
PODKREŚLAJĄC : To, że MOŻESZ używać protokołu mapowania, NIE oznacza, że POWINIENEŚ to zrobić . Czy rzeczywiście ma sens przekazywanie obiektu jako zestawu par klucz-wartość, czy też jako argumenty i wartości słów kluczowych? Czy dostęp do niego za pomocą klucza - podobnie jak w słowniku - naprawdę ma sens?
Jeśli odpowiedź na te pytania jest twierdząca , prawdopodobnie warto rozważyć następną opcję.
Opcja 4: przyjrzyj się użyciu 'collections.abcmodułu „ .
Dziedziczenie Twojej klasy od 'collections.abc.Mappinglub 'collections.abc.MutableMappingsygnalizuje innym użytkownikom, że pod każdym względem Twoja klasa jest mapowaniem * i można oczekiwać, że będzie się w ten sposób zachowywać.
Nadal możesz rzucić swój obiekt na dictdokładnie taki, jaki potrzebujesz, ale prawdopodobnie nie miałoby to żadnego powodu. Ze względu na pisanie kaczych kłopotów z rzutowaniem obiektu mapującego na a dictbyłoby przez większość czasu dodatkowym, niepotrzebnym krokiem.
Ta odpowiedź może być również pomocna.
Jak zauważono w komentarzach poniżej: warto wspomnieć, że robienie tego w sposób abc zasadniczo zmienia twoją klasę obiektów w klasę dictpodobną do a (zakładając, że używasz, MutableMappinga nie Mappingklasy bazowej tylko do odczytu ). Wszystko, z czym byłbyś w stanie zrobić dict, mógłbyś zrobić z własnym obiektem klasy. Może to być pożądane lub nie.
Weź również pod uwagę numeryczne abcs w numbersmodule:
https://docs.python.org/3/library/numbers.html
Ponieważ rzucasz również swój obiekt na obiekt int, bardziej sensowne może być przekształcenie swojej klasy w pełnoprawną int, aby rzucanie nie było konieczne.
Opcja 5: przyjrzyj się używaniu dataclassesmodułu (tylko Python 3.7), który zawiera wygodną asdict()metodę narzędziową.
from dataclasses import dataclass, asdict, field, InitVar
@dataclass
class Wharrgarbl(object):
a: int
b: int
c: int
sum: InitVar[int]
version: InitVar[str] = "old"
def __post_init__(self, sum, version):
self.sum = 6
self.version = str(version)
Teraz możesz to zrobić:
>>> asdict(Wharrgarbl(1,2,3,4,"X"))
{'a': 1, 'b': 2, 'c': 3}
Opcja 6: Użyj typing.TypedDict, która została dodana w Pythonie 3.8 .
UWAGA: opcja 6 prawdopodobnie NIE jest tym, czego szuka OP lub inni czytelnicy w oparciu o tytuł tego pytania. Zobacz dodatkowe komentarze poniżej.
class Wharrgarbl(TypedDict):
a: str
b: str
c: str
Używając tej opcji, wynikowy obiekt to adict (wyróżnienie: to nie jest a Wharrgarbl). Nie ma żadnego powodu, by „przesyłać” to na dyktando (chyba że robisz kopię).
A ponieważ obiekt to adict , sygnatura inicjalizacji jest identyczna jak w dicti jako taka akceptuje tylko argumenty słów kluczowych lub inny słownik.
>>> w = Wharrgarbl(a=1,b=2,b=3)
>>> w
{'a': 1, 'b': 2, 'c': 3}
>>> type(w)
<class 'dict'>
Podkreślenie : powyższa „klasa” Wharrgarblnie jest w rzeczywistości nową klasą. Jest to po prostu cukier syntaktyczny do tworzenia dictobiektów wpisanych z polami różnych typów dla narzędzia do sprawdzania typów.
Jako taka, ta opcja może być całkiem wygodna do sygnalizowania czytelnikom twojego kodu (a także kontrolerom typów, takim jak mypy), że dictoczekuje się, że taki obiekt będzie miał określone klucze o określonych typach wartości.
Ale to oznacza, że nie możesz na przykład dodać innych metod, chociaż możesz spróbować:
class MyDict(TypedDict):
def my_fancy_method(self):
return "world changing result"
... ale to nie zadziała:
>>> MyDict().my_fancy_method()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'my_fancy_method'
* „Mapowanie” stało się standardową „nazwą” dicttypu kaczki