Existem pelo menos cinco seis maneiras. A forma preferida depende de qual é o seu caso de uso.
Opção 1: basta adicionar um asdict()método.
Com base na descrição do problema, eu consideraria bastante a asdictmaneira de fazer as coisas sugerida por outras respostas. Isso ocorre porque não parece que seu objeto seja realmente uma coleção:
class Wharrgarbl(object):
...
def asdict(self):
return {'a': self.a, 'b': self.b, 'c': self.c}
Usar as outras opções abaixo pode ser confuso para os outros, a menos que seja muito óbvio exatamente quais membros do objeto seriam e não seriam iterados ou especificados como pares chave-valor.
Opção 1a: Herdar sua classe 'typing.NamedTuple'(ou o equivalente principalmente 'collections.namedtuple') e usar o _asdictmétodo fornecido para você.
from typing import NamedTuple
class Wharrgarbl(NamedTuple):
a: str
b: str
c: str
sum: int = 6
version: str = 'old'
Usar uma tupla nomeada é uma maneira muito conveniente de adicionar muitas funcionalidades à sua classe com um mínimo de esforço, incluindo um _asdictmétodo . No entanto, uma limitação é que, conforme mostrado acima, o NT incluirá todos os seus membros _asdict.
Se houver membros que você não deseja incluir em seu dicionário, você precisará modificar o _asdictresultado:
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
Outra limitação é que o NT é somente leitura. Isso pode ou não ser desejável.
Opção 2: Implementar __iter__.
Assim, por exemplo:
def __iter__(self):
yield 'a', self.a
yield 'b', self.b
yield 'c', self.c
Agora você pode apenas fazer:
dict(my_object)
Isso funciona porque o dict()construtor aceita um iterável de (key, value)pares para construir um dicionário. Antes de fazer isso, pergunte a si mesmo se iterar o objeto como uma série de pares de chave e valor dessa maneira - embora seja conveniente para criar um dict- pode realmente ser um comportamento surpreendente em outros contextos. Por exemplo, pergunte a si mesmo "qual deveria ser o comportamento de list(my_object)...?"
Além disso, observe que acessar valores diretamente usando a obj["a"]sintaxe get item não funcionará e a descompactação de argumento de palavra-chave não funcionará. Para eles, você precisa implementar o protocolo de mapeamento.
Opção 3: implemente o protocolo de mapeamento . Isso permite o comportamento de acesso por chave, lançando para um dictsem usar __iter__, e também fornece comportamento de descompactação ( {**my_obj}) e comportamento de descompactação de palavra-chave se todas as chaves forem strings ( dict(**my_obj)).
O protocolo de mapeamento requer que você forneça (no mínimo) dois métodos juntos: keys()e __getitem__.
class MyKwargUnpackable:
def keys(self):
return list("abc")
def __getitem__(self, key):
return dict(zip("abc", "one two three".split()))[key]
Agora você pode fazer coisas como:
>>> m=MyKwargUnpackable()
>>> m["a"]
'one'
>>> dict(m)
{'a': 'one', 'b': 'two', 'c': 'three'}
>>> dict(**m)
{'a': 'one', 'b': 'two', 'c': 'three'}
Como mencionado acima, se você estiver usando uma versão nova o suficiente de python, você também pode descompactar seu objeto de protocolo de mapeamento em uma compreensão de dicionário como esta (e neste caso não é necessário que suas chaves sejam strings):
>>> {**m}
{'a': 'one', 'b': 'two', 'c': 'three'}
Observe que o protocolo de mapeamento tem precedência sobre o __iter__método ao lançar um objeto para um dictdiretamente (sem usar o desempacotamento kwarg, isto é dict(m)). Portanto, é possível - e às vezes conveniente - fazer com que o objeto tenha um comportamento diferente quando usado como um iterável (por exemplo, list(m)) vs. quando convertido para a dict( dict(m)).
DESTACADO : Só porque você PODE usar o protocolo de mapeamento, NÃO significa que você DEVE fazê-lo . Faz sentido que seu objeto seja transmitido como um conjunto de pares de valores-chave ou como argumentos e valores de palavras-chave? Acessar por chave - assim como um dicionário - realmente faz sentido?
Se a resposta a essas perguntas for sim , provavelmente é uma boa ideia considerar a próxima opção.
Opção 4: tente usar o 'collections.abcmódulo ' .
Herdar sua classe de 'collections.abc.Mappingou 'collections.abc.MutableMappingsinaliza para outros usuários que, para todos os efeitos, sua classe é um mapeamento * e pode-se esperar que se comporte dessa maneira.
Você ainda pode lançar seu objeto para um dictconforme necessário, mas provavelmente haveria poucos motivos para isso. Por causa da digitação de pato , incomodar-se em lançar seu objeto de mapeamento para a dictseria apenas uma etapa adicional desnecessária na maioria das vezes.
Essa resposta também pode ser útil.
Como observado nos comentários abaixo: vale a pena mencionar que fazer isso da maneira abc essencialmente transforma sua classe de objeto em uma dictclasse semelhante (assumindo que você usa MutableMappinge não a Mappingclasse base somente leitura ). Tudo com o que você seria capaz de fazer dict, você poderia fazer com seu próprio objeto de classe. Isso pode ser, ou não, desejável.
Considere também observar os abcs numéricos no numbersmódulo:
https://docs.python.org/3/library/numbers.html
Como você também está lançando seu objeto para um int, pode fazer mais sentido essencialmente transformar sua classe em um completo intpara que o lançamento não seja necessário.
Opção 5: tente usar o dataclassesmódulo (somente Python 3.7), que inclui um asdict()método utilitário conveniente .
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)
Agora você pode fazer isso:
>>> asdict(Wharrgarbl(1,2,3,4,"X"))
{'a': 1, 'b': 2, 'c': 3}
Opção 6: uso typing.TypedDict, que foi adicionado ao python 3.8 .
NOTA: a opção 6 provavelmente NÃO é o que o OP ou outros leitores com base no título desta pergunta estão procurando. Veja comentários adicionais abaixo.
class Wharrgarbl(TypedDict):
a: str
b: str
c: str
Usando esta opção, o objeto resultante é umadict (ênfase: é não um Wharrgarbl). Não há razão alguma para "convertê-lo" em um dicionário (a menos que você esteja fazendo uma cópia).
E como o objeto é adict , a assinatura de inicialização é idêntica à de dicte, como tal, só aceita argumentos de palavra-chave ou outro dicionário.
>>> w = Wharrgarbl(a=1,b=2,b=3)
>>> w
{'a': 1, 'b': 2, 'c': 3}
>>> type(w)
<class 'dict'>
Enfatizado : a "classe" acima Wharrgarblnão é realmente uma classe nova. É simplesmente um açúcar sintático para criar dictobjetos digitados com campos de tipos diferentes para o verificador de tipo.
Como tal, essa opção pode ser muito conveniente para sinalizar aos leitores de seu código (e também a um verificador de tipo como mypy) que dictse espera que esse objeto tenha chaves específicas com tipos de valor específicos.
Mas isso significa que você não pode, por exemplo, adicionar outros métodos, embora possa tentar:
class MyDict(TypedDict):
def my_fancy_method(self):
return "world changing result"
... mas não vai funcionar:
>>> MyDict().my_fancy_method()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'my_fancy_method'
* "Mapeamento" tornou-se o "nome" padrão do dicttipo de pato semelhante