Ci sono almeno cinque sei modi. Il modo preferito dipende dal tuo caso d'uso.
Opzione 1: aggiungi semplicemente un asdict()metodo.
Sulla base della descrizione del problema, prenderei in considerazione molto il asdictmodo di fare le cose suggerito da altre risposte. Questo perché non sembra che il tuo oggetto sia davvero una collezione:
class Wharrgarbl(object):
...
def asdict(self):
return {'a': self.a, 'b': self.b, 'c': self.c}
L'utilizzo delle altre opzioni di seguito potrebbe creare confusione per gli altri, a meno che non sia molto ovvio esattamente quali membri dell'oggetto verrebbero e non sarebbero iterati o specificati come coppie chiave-valore.
Opzione 1a: eredita la tua classe da 'typing.NamedTuple'(o la maggior parte equivalente 'collections.namedtuple') e utilizza il _asdictmetodo fornito per te.
from typing import NamedTuple
class Wharrgarbl(NamedTuple):
a: str
b: str
c: str
sum: int = 6
version: str = 'old'
L'uso di una tupla con nome è un modo molto conveniente per aggiungere molte funzionalità alla classe con il minimo sforzo, incluso un _asdictmetodo . Tuttavia, una limitazione è che, come mostrato sopra, il NT includerà tutti i membri nel suo file _asdict.
Se ci sono membri che non desideri includere nel dizionario, dovrai modificare il _asdictrisultato:
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
Un'altra limitazione è che NT è di sola lettura. Questo può o non può essere desiderabile.
Opzione 2: implementare __iter__.
In questo modo, ad esempio:
def __iter__(self):
yield 'a', self.a
yield 'b', self.b
yield 'c', self.c
Ora puoi semplicemente fare:
dict(my_object)
Questo funziona perché il dict()costruttore accetta un iterabile di (key, value)coppie per costruire un dizionario. Prima di fare ciò, chiediti se l'iterazione dell'oggetto come una serie di coppie di chiavi e valori in questo modo, mentre è conveniente per creare un dict, potrebbe effettivamente essere un comportamento sorprendente in altri contesti. Ad esempio, poniti la domanda "quale dovrebbe essere il comportamento di list(my_object)...?"
Inoltre, tieni presente che l'accesso diretto ai valori utilizzando la obj["a"]sintassi get item non funzionerà e lo spacchettamento degli argomenti delle parole chiave non funzionerà. Per quelli, dovresti implementare il protocollo di mappatura.
Opzione 3: implementare il protocollo di mappatura . Ciò consente il comportamento di accesso tramite chiave, il cast a un dictsenza l'utilizzo __iter__e fornisce anche il comportamento di decompressione ( {**my_obj}) e il comportamento di decompressione delle parole chiave se tutte le chiavi sono stringhe ( dict(**my_obj)).
Il protocollo di mappatura richiede che tu fornisca (almeno) due metodi insieme: keys()e __getitem__.
class MyKwargUnpackable:
def keys(self):
return list("abc")
def __getitem__(self, key):
return dict(zip("abc", "one two three".split()))[key]
Ora puoi fare cose come:
>>> m=MyKwargUnpackable()
>>> m["a"]
'one'
>>> dict(m)
{'a': 'one', 'b': 'two', 'c': 'three'}
>>> dict(**m)
{'a': 'one', 'b': 'two', 'c': 'three'}
Come accennato in precedenza, se stai usando una versione abbastanza nuova di python puoi anche decomprimere il tuo oggetto protocollo di mappatura in una comprensione del dizionario in questo modo (e in questo caso non è necessario che le tue chiavi siano stringhe):
>>> {**m}
{'a': 'one', 'b': 'two', 'c': 'three'}
Si noti che il protocollo mappatura prevale sulla il __iter__metodo quando esprimono un oggetto a un dictdirettamente (senza utilizzare kwarg disimballaggio, cioè dict(m)). Quindi è possibile, e talvolta conveniente, fare in modo che l'oggetto abbia un comportamento diverso quando usato come iterabile (ad esempio list(m)) rispetto a quando viene lanciato a dict( dict(m)).
SOTTOLINEATO : Solo perché PUOI usare il protocollo di mappatura, NON significa che DOVRESTI farlo . Ha davvero senso che il tuo oggetto venga trasmesso come un insieme di coppie chiave-valore o come argomenti e valori di parole chiave? Accedervi per chiave, proprio come un dizionario, ha davvero senso?
Se la risposta a queste domande è sì , è probabilmente una buona idea considerare l'opzione successiva.
Opzione 4: cerca di utilizzare il 'collections.abcmodulo " .
Ereditare la tua classe 'collections.abc.Mappingo 'collections.abc.MutableMappingsegnala ad altri utenti che, a tutti gli effetti, la tua classe è una mappatura * e ci si può aspettare che si comporti in quel modo.
Puoi ancora lanciare il tuo oggetto su un dictcome richiesto, ma probabilmente ci sarebbero pochi motivi per farlo. A causa della dattilografia , preoccuparsi di lanciare il proprio oggetto di mappatura su a dictsarebbe solo un passaggio aggiuntivo non necessario per la maggior parte del tempo.
Questa risposta potrebbe anche essere utile.
Come notato nei commenti seguenti: vale la pena ricordare che facendo questo in modo abc si trasforma essenzialmente la classe di oggetti in una dictclasse simile (supponendo che si usi MutableMappinge non la Mappingclasse base di sola lettura ). Tutto ciò che potresti fare con dict, potresti farlo con il tuo oggetto di classe. Questo può essere o non essere desiderabile.
Considera anche di guardare gli abcs numerici nel numbersmodulo:
https://docs.python.org/3/library/numbers.html
Dal momento che stai anche lanciando il tuo oggetto su un int, potrebbe avere più senso trasformare essenzialmente la tua classe in una vera e propria classe in intmodo che il casting non sia necessario.
Opzione 5: cerca di utilizzare il dataclassesmodulo (solo Python 3.7), che include un comodo asdict()metodo di utilità.
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)
Ora puoi farlo:
>>> asdict(Wharrgarbl(1,2,3,4,"X"))
{'a': 1, 'b': 2, 'c': 3}
Opzione 6: Usa typing.TypedDict, che è stata aggiunta in Python 3.8 .
NOTA: l'opzione 6 probabilmente NON è ciò che l'OP, o altri lettori in base al titolo di questa domanda, stanno cercando. Vedere ulteriori commenti di seguito.
class Wharrgarbl(TypedDict):
a: str
b: str
c: str
Utilizzando questa opzione, l'oggetto risultante è undict (enfasi: esso è non una Wharrgarbl). Non c'è alcun motivo per "lanciarlo" su un dict (a meno che tu non ne stia facendo una copia).
E poiché l'oggetto è adict , la firma di inizializzazione è identica a quella di dicte come tale accetta solo argomenti di parole chiave o un altro dizionario.
>>> w = Wharrgarbl(a=1,b=2,b=3)
>>> w
{'a': 1, 'b': 2, 'c': 3}
>>> type(w)
<class 'dict'>
Sottolineato : la "classe" di cui sopra Wharrgarblnon è affatto una nuova classe. È semplicemente zucchero sintattico per creare dictoggetti digitati con campi di diversi tipi per il controllo del tipo.
In quanto tale, questa opzione può essere piuttosto comoda per segnalare ai lettori del codice (e anche a un controllore di tipo come mypy) che dictci si aspetta che un tale oggetto abbia chiavi specifiche con tipi di valore specifici.
Ma questo significa che non puoi, ad esempio, aggiungere altri metodi, anche se puoi provare:
class MyDict(TypedDict):
def my_fancy_method(self):
return "world changing result"
... ma non funzionerà:
>>> MyDict().my_fancy_method()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'my_fancy_method'
* "Mappatura" è diventato il "nome" standard del dicttipo di anatra