Ответ 1
Если вы на самом деле никогда не определяете какие-либо методы класса, на мой взгляд, более разумный аргумент dict или namedtuple. Простой + встроенный - это хорошо! Тем не менее каждый из них.
Иногда имеет смысл группировать связанные данные вместе. Я имею тенденцию делать это с помощью dict, например,
self.group = dict(a=1, b=2, c=3)
print self.group['a']
Один из моих коллег предпочитает создавать класс
class groupClass(object):
def __init__(a, b, c):
self.a = a
self.b = b
self.c = c
self.group = groupClass(1, 2, 3)
print self.group.a
Обратите внимание, что мы не определяем какие-либо методы класса.
Мне нравится использовать dict, потому что мне нравится минимизировать количество строк кода. Мой коллега считает, что код более читабельен, если вы используете класс, и это облегчает добавление методов в класс в будущем.
Что вы предпочитаете и почему?
Если вы на самом деле никогда не определяете какие-либо методы класса, на мой взгляд, более разумный аргумент dict или namedtuple. Простой + встроенный - это хорошо! Тем не менее каждый из них.
Фон
Резюме альтернативных контейнеров данных на основе атрибутов было представлено Р. Хеттингером на встрече SF Python 2017 Holiday. Посмотрите его твит и его слайд-колоду. Он также выступил с докладом на PyCon 2018 о классах данных.
Другие типы контейнеров данных упоминаются в этой статье и преимущественно в документации по Python 3 (см. ссылки ниже).
Вот обсуждение списка рассылки python-ideas о добавлении recordclass
в стандартную библиотеку.
параметры
Альтернативы в стандартной библиотеке
collections.namedtuple
: кортеж с атрибутами (см. оригинальный рецепт)typing.NamedTuple
: подклассифицированный кортеж (см. этот пост, сравнивая его с namedtuple
)types.SimpleNamespace
: простой класс с необязательным объявлением классаtypes.MappingProxy
: диктант только для чтенияenum.Enum
: ограниченный набор связанных констант (ведет себя как класс)dataclasses.dataclass
: изменяемый именованный кортеж с классами по умолчанию/без шаблоновВнешние параметры
SimpleNamedspace
; см. также munch
(py3))Какой?
Решение, какой вариант использовать, зависит от ситуации (см. примеры ниже). Обычно достаточно старомодный изменяемый словарь или неизменный именованный набор. Классы данных - это новейшее дополнение (Python 3.7a), предлагающее как изменчивость, так и необязательную неизменность, с обещанием уменьшения стандартного шаблона, вдохновленного проектом attrs.
Примеры
import typing as typ
import collections as ct
import dataclasses as dc
# Problem: You want a simple container to hold personal data.
# Solution: Try a NamedTuple.
>>> class Person(typ.NamedTuple):
... name: str
... age: int
>>> a = Person("bob", 30)
>>> a
Person(name='bob', age=30)
# Problem: You need to change age each year, but namedtuples are immutable.
# Solution: Use assignable attributes of a traditional class.
>>> class Person:
... def __init__(self, name, age):
... self.name = name
... self.age = age
>>> b = Person("bob", 30)
>>> b.age = 31
>>> b
<__main__.Person at 0x4e27128>
# Problem: You lost the pretty repr and want to add comparison features.
# Solution: Use included repr and eq features from the new dataclasses.
>>> @dc.dataclass(eq=True)
... class Person:
... name: str
... age: int
>>> c = Person("bob", 30)
>>> c.age = 31
>>> c
Person(name='bob', age=31)
>>> d = Person("dan", 31)
>>> c != d
True
Я предпочитаю следовать YAGNI и использовать dict.
Существует новое предложение, направленное на реализацию именно того, что вы ищете, под названием классы данных. Посмотрите на это.
Использование класса над диктовкой - вопрос предпочтений. Лично я предпочитаю использовать dict, когда ключи не известны априори. (Как картографический контейнер).
Использование класса для хранения данных означает, что вы можете предоставить документацию для атрибутов класса.
Лично, возможно, самая главная причина для меня использовать класс - использовать функцию автозаполнения IDE! (технически неубедительная причина, но очень полезная на практике)
Твой путь лучше. Не пытайтесь предвидеть будущее слишком сильно, поскольку вряд ли у вас получится.
Однако иногда имеет смысл использовать что-то вроде структуры C, например, если вы хотите идентифицировать разные типы, а не использовать dicts для всего.
Кстати, я думаю, что реализованный в Python 3.7 @класс данных - самый простой и эффективный способ реализации классов в качестве контейнеров данных.
@dataclass
class Data:
a: list
b: str #default variables go after non default variables
c: bool = False
def func():
return A(a="hello")
print(func())
Вывод будет: hello
Это слишком похоже на Scala, как case-класс и самый простой способ использовать класс в качестве контейнера.
Вы можете комбинировать преимущества dict и class вместе, используя некоторый класс оболочки, унаследованный от dict. Вам не нужно писать шаблонный код и в то же время использовать точечную нотацию.
class ObjDict(dict):
def __getattr__(self,attr):
return self[attr]
def __setattr__(self,attr,value):
self[attr]=value
self.group = ObjDict(a=1, b=2, c=3)
print self.group.a
Я не согласен с тем, что код более читабельен с использованием класса без методов. Обычно вы ожидаете функциональности от класса, а не только от данных.
Итак, я бы пошел на диктофон, пока не возникнет необходимость в функциональности, и тогда конструктор класса может получить dict: -)
В языке, который поддерживает его, я бы использовал struct
. Словарь будет ближе всего к структуре в Python, по крайней мере, насколько я вижу.
Не говоря уже о том, что вы могли бы добавить метод в словарь в любом случае, если вы действительно хотели;)
Очевидно, что диктум подходит для этой ситуации. Он был разработан специально для этого случая использования. Если вы на самом деле не собираетесь использовать класс в качестве класса, нет смысла повторно изобретать колесо и налагать дополнительные накладные расходы/тратить пространство класса, который действует как плохой словарь (никаких функций словаря).
Как насчет Prodict:
group = Prodict(a=1, b=2, c=3)
group.d = 4
А если вам нужно автоматическое преобразование типов и автоматическое завершение кода (в смысле intelli):
class Person(Prodict):
name: str
email: str
rate: int
john = Person(name='John', email='[email protected]')
john.rate = 7
john.age = 35 # dynamic
Если кто-то не заботится об объеме памяти, то dict, namedtuple, dataclass или просто класс с __slots__
- хороший выбор.
Но если нужно создать миллионы объектов с несколькими атрибутами, тогда есть решение на основе recordclass библиотеки:
from recordclass import make_dataclass
C = make_dataclass("C", ('a', 'b', 'c'))
c = C(1, 2, 3)
То же самое с определением класса:
from recordclass import dataobject
class C(dataobject):
a:int
b:int
c:int
c = C(1, 2, 3)
У него минимальный объем памяти = sizeof(PyObject_HEAD) + 3*sizeof(PyObject*)
байт.
Для сравнения вариант __slots__
-based требует sizeof(PyGC_Head) + sizeof(PyObject_HEAD) + 3*sizeof(PyObject*)
байтов.