Подтвердить что ты не робот

Использование класса Python в качестве контейнера данных

Иногда имеет смысл группировать связанные данные вместе. Я имею тенденцию делать это с помощью 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, потому что мне нравится минимизировать количество строк кода. Мой коллега считает, что код более читабельен, если вы используете класс, и это облегчает добавление методов в класс в будущем.

Что вы предпочитаете и почему?

4b9b3361

Ответ 1

Если вы на самом деле никогда не определяете какие-либо методы класса, на мой взгляд, более разумный аргумент dict или namedtuple. Простой + встроенный - это хорошо! Тем не менее каждый из них.

Ответ 2

Фон

Резюме альтернативных контейнеров данных на основе атрибутов было представлено Р. Хеттингером на встрече SF Python 2017 Holiday. Посмотрите его твит и его слайд-колоду. Он также выступил с докладом на PyCon 2018 о классах данных.

Другие типы контейнеров данных упоминаются в этой статье и преимущественно в документации по Python 3 (см. ссылки ниже).

Вот обсуждение списка рассылки python-ideas о добавлении recordclass в стандартную библиотеку.

параметры

Альтернативы в стандартной библиотеке

  • collections.namedtuple: кортеж с атрибутами (см. оригинальный рецепт)
  • typing.NamedTuple: подклассифицированный кортеж (см. этот пост, сравнивая его с namedtuple)
  • types.SimpleNamespace: простой класс с необязательным объявлением класса
  • types.MappingProxy: диктант только для чтения
  • enum.Enum: ограниченный набор связанных констант (ведет себя как класс)
  • dataclasses.dataclass: изменяемый именованный кортеж с классами по умолчанию/без шаблонов

Внешние параметры

  • записи: изменяемый именованный кортеж (см. также recordclass)
  • Связка: добавить атрибут доступа к диктовкам (вдохновение для SimpleNamedspace; см. также munch (py3))
  • поле: обтекание текста с помощью функции поиска в стиле точек functionality
  • attrdict: доступ к элементам из сопоставления в виде ключей или атрибутов
  • поля: удалить шаблон из классов контейнеров.
  • namedlist: непостоянные, похожие на кортежи контейнеры со значениями по умолчанию Э. Смитом
  • misc.: сообщения о создании собственной структуры, объекта, связки, dict-прокси и т.д.

Какой?

Решение, какой вариант использовать, зависит от ситуации (см. примеры ниже). Обычно достаточно старомодный изменяемый словарь или неизменный именованный набор. Классы данных - это новейшее дополнение (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

Ответ 3

Я предпочитаю следовать YAGNI и использовать dict.

Ответ 4

Существует новое предложение, направленное на реализацию именно того, что вы ищете, под названием классы данных. Посмотрите на это.

Использование класса над диктовкой - вопрос предпочтений. Лично я предпочитаю использовать dict, когда ключи не известны априори. (Как картографический контейнер).

Использование класса для хранения данных означает, что вы можете предоставить документацию для атрибутов класса.

Лично, возможно, самая главная причина для меня использовать класс - использовать функцию автозаполнения IDE! (технически неубедительная причина, но очень полезная на практике)

Ответ 6

Кстати, я думаю, что реализованный в 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-класс и самый простой способ использовать класс в качестве контейнера.

Ответ 7

Вы можете комбинировать преимущества 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

Ответ 8

Я не согласен с тем, что код более читабельен с использованием класса без методов. Обычно вы ожидаете функциональности от класса, а не только от данных.

Итак, я бы пошел на диктофон, пока не возникнет необходимость в функциональности, и тогда конструктор класса может получить dict: -)

Ответ 9

В языке, который поддерживает его, я бы использовал struct. Словарь будет ближе всего к структуре в Python, по крайней мере, насколько я вижу.

Не говоря уже о том, что вы могли бы добавить метод в словарь в любом случае, если вы действительно хотели;)

Ответ 10

Очевидно, что диктум подходит для этой ситуации. Он был разработан специально для этого случая использования. Если вы на самом деле не собираетесь использовать класс в качестве класса, нет смысла повторно изобретать колесо и налагать дополнительные накладные расходы/тратить пространство класса, который действует как плохой словарь (никаких функций словаря).

Ответ 11

Как насчет 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

Ответ 12

Если кто-то не заботится об объеме памяти, то 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*) байтов.