Это еще один случай pylintслепых правил России.
«Классы не предназначены для хранения данных» - это ложное утверждение. Словари подходят не для всего. Член данных класса - это что-то значимое, элемент словаря - это нечто необязательное. Доказательство: вы можете сделать, dictionary.get('key', DEFAULT_VALUE)чтобы предотвратить a KeyError, но по __getattr__умолчанию нет простого .
EDIT - рекомендуемые способы использования структур
Мне нужно обновить свой ответ. Прямо сейчас - если вам нужен struct, у вас есть два отличных варианта:
а) Просто используйте attrs
Это библиотека для этого:
https://www.attrs.org/en/stable/
import attr
@attr.s
class MyClass(object): # or just MyClass: for Python 3
foo = attr.ib()
bar = attr.ib()
Что вы получаете дополнительно: отказ от написания конструкторов, значений по умолчанию, проверки, __repr__объекты только для чтения (для замены namedtuples, даже в Python 2) и многое другое.
б) Используйте dataclasses(Py 3.7+)
Следуя комментарию hwjp, я также рекомендую dataclasses:
https://docs.python.org/3/library/dataclasses.html
Это почти так же хорошо, как attrsи стандартный библиотечный механизм («батарейки включены»), без дополнительных зависимостей, кроме Python 3.7+.
Остальная часть предыдущего ответа
NamedTupleне очень хорошо - особенно до Python 3 typing.NamedTuple:
https://docs.python.org/3/library/typing.html#typing.NamedTuple
- вам определенно следует проверить NamedTupleшаблон «класс, производный от ». Python 2, namedtuplesсозданный из строковых описаний, уродлив, плох, а "программирование внутри строковых литералов" глупо.
Я согласен с двумя текущими ответами («подумайте об использовании чего-то еще, но pylint не всегда правильный» - принятый и «используйте комментарий, подавляющий pylint»), но у меня есть собственное предложение.
Позвольте мне указать на это еще раз: некоторые классы предназначены только для хранения данных.
Теперь возможность также рассмотреть - используйте property-ies.
class MyClass(object):
def __init__(self, foo, bar):
self._foo = foo
self._bar = bar
@property
def foo(self):
return self._foo
@property
def bar(self):
return self._bar
Выше у вас есть свойства только для чтения, которые подходят для объекта значения (например, как в доменном дизайне), но вы также можете предоставить сеттеры - таким образом ваш класс сможет взять на себя ответственность за поля, которые у вас есть - например для выполнения некоторой проверки и т. д. (если у вас есть сеттеры, вы можете назначить их использование в конструкторе, то есть self.foo = fooвместо прямого self._foo = foo, но осторожного, сеттеры могут предположить, что другие поля уже инициализированы, и тогда вам понадобится настраиваемая проверка в конструкторе) .