Ответ 1
Существует как минимум пять шести способов. Предпочтительный способ зависит от вашего варианта использования.
Вариант 1: Просто добавьте метод asdict()
.
Основываясь на описании проблемы, я бы очень хотел рассмотреть способ действий asdict
, предложенный другими ответами. Это потому, что не кажется, что ваш объект действительно большая часть коллекции:
class Wharrgarbl(object):
...
def asdict(self):
return {'a': self.a, 'b': self.b, 'c': self.c}
Использование других параметров, приведенных ниже, может сбивать с толку других, если не очень ясно, какие именно члены объекта будут и не будут повторяться или указываться в качестве пар значений key-.
Вариант 1a: Используйте метод _asdict
, предоставляемый 'typing.NamedTuple'
(или в основном эквивалентным 'collections.namedtuple'
).
Использование именованного кортежа - это очень удобный способ добавить множество функций в ваш класс с минимальными усилиями, включая метод _asdict
. Тем не менее, ограничение состоит в том, что NT будет включать всех членов в _asdict
. Поэтому, если есть участники, которых вы не хотите включать, вам нужно изменить результат _asdict
:
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
Другое ограничение - NT только для чтения. Это может или не может быть желательным.
Вариант 2: Реализация __iter__
.
Вот так, например:
def __iter__(self):
yield 'a', self.a
yield 'b', self.b
yield 'c', self.c
Теперь вы можете просто сделать:
dict(my_object)
Это работает, потому что конструктор dict()
принимает итерацию пар (key, value)
для создания словаря. Прежде чем сделать это, задайте себе вопрос, может ли итерация объекта в виде последовательности пар ключ-значение в этом manner-, хотя и удобная для создания dict
, - на самом деле может быть удивительным поведением в других контекстах. Например, задайте себе вопрос "каким должно быть поведение list(my_object)
...?"
Кроме того, обратите внимание, что прямой доступ к значениям с использованием синтаксиса get item obj["a"]
не будет работать, а распаковка аргументов ключевых слов не будет работать. Для этого вам необходимо реализовать протокол сопоставления.
Вариант 3: Реализация протокола сопоставления mapping protocol. Это позволяет вести доступ по ключу, приводить к dict
без использования __iter__
, а также обеспечивает поведение при распаковке ключевых слов.
Протокол сопоставления требует, чтобы вы предоставили (как минимум) два метода вместе: keys()
и __getitem__
.
class MyKwargUnpackable:
def keys(self):
return list("abc")
def __getitem__(self, key):
return dict(zip("abc", "one two three".split()))[key]
Теперь вы можете делать такие вещи, как:
>>> m=MyKwargUnpackable()
>>> m["a"]
'one'
>>> dict(m) # cast to dict directly
{'a': 'one', 'b': 'two', 'c': 'three'}
>>> dict(**m) # unpack as kwargs
{'a': 'one', 'b': 'two', 'c': 'three'}
Если вы используете достаточно новую версию python, вы также можете распаковать свой объект протокола сопоставления в словарное представление следующим образом (и в этом случае не обязательно, чтобы ваши ключи были строками):
>>> {**m}
{'a': 'one', 'b': 'two', 'c': 'three'}
Обратите внимание, что протокол отображения имеет приоритет над методом __iter__
при непосредственном приведении объекта к dict
(без использования распаковки kwarg, т.е. dict(m)
). Таким образом, possible-, а иногда и convenient- заставляют объект работать по-разному при использовании в качестве итерируемого (например, list(m)
) по сравнению с приведением к dict
(dict(m)
).
ПОДТВЕРЖДЕНО. То, что вы МОЖЕТЕ использовать протокол сопоставления, НЕ означает, что вы ДОЛЖНЫ делать это. Имеет ли смысл передавать ваш объект в виде набора пар значений key- или в качестве аргументов и значений ключевых слов? Имеет ли смысл доступ к нему с помощью key- так же, как dictionary-?
Если ответ на эти вопросы положительный, возможно, стоит рассмотреть следующий вариант.
Вариант 4: изучите использование модуля 'collections.abc
'.
Наследование вашего класса от 'collections.abc.Mapping
или 'collections.abc.MutableMapping
сигнализирует другим пользователям о том, что для всех ваших целей и целей ваш класс является отображением *, и можно ожидать, что он будет вести себя таким образом.
Вы все еще можете привести свой объект к dict
так, как вам нужно, но, вероятно, для этого не будет особых причин. Из-за утки, набирающей, в большинстве случаев потрудиться привести ваш объект сопоставления к dict
будет просто дополнительным ненужным шагом.
Этот ответ также может быть полезным.
Как отмечено в комментариях ниже: стоит упомянуть, что при этом способ abc по сути превращает ваш объектный класс в класс dict
-like (при условии, что вы используете MutableMapping
, а не базовый класс только для чтения Mapping
). Все, что вы сможете сделать с dict
, вы можете сделать со своим собственным объектом класса. Это может быть или не быть желательным.
Также обратите внимание на числовые abcs в модуле numbers
:
https://docs.python.org/3/library/numbers.html
Поскольку вы также приводите свой объект к int
, возможно, имеет смысл превратить ваш класс в полноценный int
, чтобы приведение не было необходимости.
Вариант 5: Изучите использование модуля dataclasses
(только Python 3.7), который включает удобный метод asdict()
.
from dataclasses import dataclass, asdict, field, InitVar
@dataclass
class Wharrgarbl(object):
a: int
b: int
c: int
sum: InitVar[int] # note: InitVar will exclude this from the dict
version: InitVar[str] = "old"
def __post_init__(self, sum, version):
self.sum = 6 # this looks like an OP mistake?
self.version = str(version)
Теперь вы можете сделать это:
>>> asdict(Wharrgarbl(1,2,3,4,"X"))
{'a': 1, 'b': 2, 'c': 3}
Вариант 6: Используйте typing.TypedDict
, который был добавлен в python 3.8.
class Wharrgarbl(TypedDict):
a: str
b: str
c: str
Используя эту опцию, результирующий объект является dict
(выделение: это не Wharrgarbl
). Нет никакой причины "приводить" его к диктату (если вы не делаете копию).
А поскольку объект является dict
, подпись инициализации идентична сигнатуре dict
и поэтому принимает только аргументы ключевых слов или другой словарь.
>>> w = Wharrgarbl(a=1,b=2,b=3)
>>> w
{'a': 1, 'b': 2, 'c': 3}
>>> type(w)
<class 'dict'>
Подчеркивание: вышеуказанный "класс" Wharrgarbl
на самом деле вовсе не новый класс. Это просто синтаксический сахар для создания типизированных объектов dict
с полями разных типов для проверки типов. Таким образом, вы не можете, например, добавить другие методы, хотя вы можете попробовать:
class MyDict(TypedDict):
def my_fancy_method(self):
return "world changing result"
... но это не сработает:
>>> MyDict().my_fancy_method()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'my_fancy_method'
ПРИМЕЧАНИЕ: вариант 6, скорее всего, НЕ тот, который ищут ОП или другие читатели, основываясь на заголовке этого вопроса. Однако эта опция может быть очень удобной для передачи читателям вашего кода (а также средству проверки типов, например, mypy), что ожидается, что такой объект dict
будет иметь определенные ключи с определенными типами значений.
* "Картирование" стало стандартным "именем" типа утки dict
-like