Hay al menos cinco seis formas. La forma preferida depende de cuál sea su caso de uso.
Opción 1: simplemente agregue un asdict()método.
Según la descripción del problema, consideraría mucho la asdictforma de hacer las cosas sugerida por otras respuestas. Esto se debe a que no parece que su objeto sea realmente una gran colección:
class Wharrgarbl(object):
...
def asdict(self):
return {'a': self.a, 'b': self.b, 'c': self.c}
El uso de las otras opciones a continuación podría resultar confuso para los demás, a menos que sea muy obvio exactamente qué miembros del objeto se iterarían y no se especificarían como pares clave-valor.
Opción 1a: herede su clase de 'typing.NamedTuple'(o la mayoría equivalente'collections.namedtuple' ) y use el _asdictmétodo proporcionado para usted.
from typing import NamedTuple
class Wharrgarbl(NamedTuple):
a: str
b: str
c: str
sum: int = 6
version: str = 'old'
Usar una tupla con nombre es una forma muy conveniente de agregar muchas funcionalidades a su clase con un mínimo de esfuerzo, incluido un_asdict método . Sin embargo, una limitación es que, como se muestra arriba, el NT incluirá a todos los miembros en su_asdict .
Si hay miembros que no desea incluir en su diccionario, deberá modificar el _asdict resultado:
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
Otra limitación es que NT es de solo lectura. Esto puede ser deseable o no.
Opcion 2: Implementar __iter__.
Así, por ejemplo:
def __iter__(self):
yield 'a', self.a
yield 'b', self.b
yield 'c', self.c
Ahora puedes simplemente hacer:
dict(my_object)
Esto funciona porque el dict()constructor acepta un iterable de(key, value) pares para construir un diccionario. Antes de hacer esto, hágase la pregunta de si iterar el objeto como una serie de pares clave-valor de esta manera, aunque es conveniente para crear un dict, podría ser un comportamiento sorprendente en otros contextos. Por ejemplo, hágase la pregunta "¿cuál debería ser el comportamiento delist(my_object) ...?"
Además, tenga en cuenta que acceder a los valores directamente utilizando el elemento obtener obj["a"] sintaxis no funcionará y el desempaquetado de argumentos de palabras clave no funcionará. Para esos, necesitaría implementar el protocolo de mapeo.
Opción 3: implementar el protocolo de mapeo . Esto permite el comportamiento de acceso por clave, emitiendo a un dictsin usar__iter__ , y también proporciona un comportamiento de desempaquetado ( {**my_obj}) y un comportamiento de desempaquetado de palabras clave si todas las claves son cadenas ( dict(**my_obj)).
El protocolo de mapeo requiere que proporcione (como mínimo) dos métodos juntos: keys()y __getitem__.
class MyKwargUnpackable:
def keys(self):
return list("abc")
def __getitem__(self, key):
return dict(zip("abc", "one two three".split()))[key]
Ahora puedes hacer cosas como:
>>> m=MyKwargUnpackable()
>>> m["a"]
'one'
>>> dict(m)
{'a': 'one', 'b': 'two', 'c': 'three'}
>>> dict(**m)
{'a': 'one', 'b': 'two', 'c': 'three'}
Como se mencionó anteriormente, si está utilizando una versión lo suficientemente nueva de Python, también puede descomprimir su objeto de protocolo de mapeo en un diccionario de comprensión como tal (y en este caso no es necesario que sus claves sean cadenas):
>>> {**m}
{'a': 'one', 'b': 'two', 'c': 'three'}
Tenga en cuenta que el protocolo de mapeo tiene prioridad sobre el __iter__método cuando se lanza un objeto a un dictdirectamente (sin usar el desempaquetado de kwarg, es decir dict(m)). Por lo tanto, es posible, y a veces conveniente, hacer que el objeto tenga un comportamiento diferente cuando se usa como iterable (por ejemplo, list(m)) frente a cuando se lanza a dict( dict(m)).
DESTACADO : El hecho de que PUEDA usar el protocolo de mapeo, NO significa que DEBE hacerlo . ¿Tiene realmente sentido que su objeto se transmita como un conjunto de pares clave-valor o como argumentos y valores de palabras clave? ¿Tiene sentido acceder a él mediante una tecla, al igual que un diccionario?
Si la respuesta a estas preguntas es afirmativa , probablemente sea una buena idea considerar la siguiente opción.
Opción 4: considere el uso del 'collections.abcmódulo ' .
Heredar su clase de 'collections.abc.Mappingo le 'collections.abc.MutableMappingindica a otros usuarios que, para todos los efectos, su clase es un mapeo * y se puede esperar que se comporte de esa manera.
Aún puede lanzar su objeto a un dicttal como lo necesite, pero probablemente haya pocas razones para hacerlo. Debido al tipo de pato , molestarse en convertir su objeto de mapeo en un dictsería un paso adicional innecesario la mayoría de las veces.
Esta respuesta también puede ser útil.
Como se indica en los comentarios a continuación: vale la pena mencionar que hacer esto de la forma abc esencialmente convierte su clase de objeto en una dictclase similar (asumiendo que usa MutableMappingy no la de solo lecturaMapping clase base de ). Todo lo que podría hacer con él dict, podría hacerlo con su propio objeto de clase. Esto puede ser deseable o no.
También considere mirar los abcs numéricos en el numbers módulo:
https://docs.python.org/3/library/numbers.html
Dado que también está lanzando su objeto a un int, podría tener más sentido convertir esencialmente su clase en unint para que el lanzamiento no sea necesario.
Opción 5: Analice el uso del dataclassesmódulo (solo Python 3.7), que incluye un asdict()método de utilidad 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)
Ahora puedes hacer esto:
>>> asdict(Wharrgarbl(1,2,3,4,"X"))
{'a': 1, 'b': 2, 'c': 3}
Opción 6: uso typing.TypedDict, que se ha agregado en python 3.8 .
NOTA: Es probable que la opción 6 NO sea lo que buscan el OP u otros lectores según el título de esta pregunta. Consulte los comentarios adicionales a continuación.
class Wharrgarbl(TypedDict):
a: str
b: str
c: str
El uso de esta opción, el objeto resultante es undict (énfasis: se no unaWharrgarbl ). No hay ninguna razón para "convertirlo" en un dictado (a menos que esté haciendo una copia).
Y dado que el objeto es adict , la firma de inicialización es idéntica a la de dicty, como tal, solo acepta argumentos de palabras clave u otro diccionario.
>>> w = Wharrgarbl(a=1,b=2,b=3)
>>> w
{'a': 1, 'b': 2, 'c': 3}
>>> type(w)
<class 'dict'>
Enfatizado : la "clase" anterior Wharrgarblno es en realidad una clase nueva en absoluto. Es simplemente azúcar sintáctico para crear tiposdict objetos con campos de diferentes tipos para el verificador de tipos.
Como tal, esta opción puede ser bastante conveniente para señalar a los lectores de su código (y también a un verificador de tipos como mypy) que dictse espera que dicho objeto tenga claves específicas con tipos de valores específicos.
Pero esto significa que no puede, por ejemplo, agregar otros métodos, aunque puede probar:
class MyDict(TypedDict):
def my_fancy_method(self):
return "world changing result"
... pero no 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'
* "Mapeo" se ha convertido en el "nombre" estándar del dicttipo -como pato