O que são classes de dados e como eles são diferentes das classes comuns?

Com PEP 557 As classes de dados são introduzidas na biblioteca padrão python.

eles fazem uso do decorador {[[0]} e eles são supostos ser "mutable namedtuples with default", mas eu realmente não tenho certeza se eu entendo o que isso realmente significa e como eles são diferentes de classes comuns.

o que são exactamente as classes de dados python e quando é melhor usá-las?

Author: wim, 2017-12-23

4 answers

As classes de dados são apenas classes regulares que são orientadas para o estado de armazenamento, mais do que conter um monte de lógica. Cada vez que você cria uma classe que consiste principalmente de atributos você fez uma classe de dados.

O que o módulo dataclasses faz é tornar mais fácil criar classes de dados. Toma conta de muita caldeira para ti.

Isto é especialmente importante quando a sua classe de dados tem de ser hashable; isto requer um método __hash__ assim como um método __eq__. Se adiciona um método personalizado __repr__ para facilitar a depuração, que pode tornar-se bastante descritivo:

class InventoryItem:
    '''Class for keeping track of an item in inventory.'''
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def __init__(
            self, 
            name: str, 
            unit_price: float,
            quantity_on_hand: int = 0
        ) -> None:
        self.name = name
        self.unit_price = unit_price
        self.quantity_on_hand = quantity_on_hand

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

    def __repr__(self) -> str:
        return (
            'InventoryItem('
            f'name={self.name!r}, unit_price={self.unit_price!r}, '
            f'quantity_on_hand={self.quantity_on_hand!r})'

    def __hash__(self) -> int:
        return hash(self.name) ^ hash(self.unit_price) ^ hash(self.quantity_on_hand)

    def __eq__(self, other) -> bool:
        if not isinstance(other, InventoryItem):
            return NotImplemented
        return (
            (self.name, self.unit_price, self.quantity_on_hand) == 
            (other.name, other.unit_price, other.quantity_on_hand))

Com {[2] } pode reduzi-lo a:

from dataclasses import dataclass

@dataclass(hash=True)
class InventoryItem:
    '''Class for keeping track of an item in inventory.'''
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

O mesmo decorador de classe também pode gerar métodos de comparação(__lt__, __gt__, etc.) and handle immutability.

namedtuple classes também são classes de dados, mas são imutáveis por padrão (bem como sendo seqüências). {[[2]} são muito mais flexíveis a este respeito, e podem ser facilmente estruturados de modo que possam {[[30]} preencher o mesmo papel que um namedtuple classe

O entusiasmo foi inspirado pelo attrs projecto , que pode fazer ainda mais (incluindo fendas, validadores, conversores, metadados, etc.).

Se você quiser ver alguns exemplos, recentemente, usei dataclasses para vários dos meus 2017 Advento do Código soluções consulte as soluções para dia 7, dia 8, dia 11 e dia 20.

Se quiser usar o módulo dataclasses nas versões Python backported (necessita de 3.6) ou utilizar o projecto attrs acima mencionado.

 34
Author: Martijn Pieters, 2018-05-17 12:27:21
[1] Btw. Raymond Hettinger (programador principal de Python) teve uma grande conversa no PyCon 2018:

Https://www.youtube.com/watch?v=T-TwcmT6Rcw&t=1390

Os slides estão aqui. https://twitter.com/raymondh/status/995693882812915712

comparison

 8
Author: Messa, 2018-06-28 11:42:13

Da especificação PEP:

É fornecido um decorador de classe que inspecciona uma definição de classe para variáveis com anotações de tipo definidas em PEP 526, " sintaxe para Variable Annotations". Neste documento, tais variáveis são chamadas campo. Usando estes campos, o decorador adiciona o método gerado definições para a classe de suporte à inicialização de instância, um repr, métodos de comparação e, facultativamente, outros métodos descritos no Secção de especificações. Tal classe é chamada de classe de dados, mas não há nada de especial na aula: o decorador acrescenta métodos gerados para a classe e retorna a mesma classe que era dado.

O gerador {[[0]} adiciona métodos à classe que se definiria de outra forma como __repr__, __init__, __lt__, e __gt__

 2
Author: Mahmoud Hossam, 2017-12-23 19:21:28

Panorâmica

A questão foi abordada. No entanto, esta resposta acrescenta alguns exemplos práticos para ajudar na compreensão básica dos dataclasses.

O que são exactamente as classes de dados python e quando é melhor usá-las?

  1. geradores de código : gerar código boilerplate; você pode optar por implementar métodos especiais numa classe regular ou ter um dataclass implementá-los automaticamente.
  2. dados containers : structures that hold data (e.g. tuplas and dicts), often with tracked, attribute access such as classes, namedtuple and others.

"mutable namedtuples with default"

Eis o que esta última frase significa:
  • mutável : por omissão, os atributos dataclass podem ser reatribuídos. Você pode opcionalmente torná-los imutáveis (veja exemplos abaixo).
  • Tem pontilhado, atributo acesso como uma aula normal.
  • predefinição : você pode atribuir valores predefinidos aos atributos

Em comparação com as classes comuns, economiza-se principalmente na escrita do Código boilerplate.


Exemplos

Aqui está uma rápida visão geral das funcionalidades do dataclass com exemplos.

O que tens

[[25]} Aqui estão as funcionalidades que você obtém por padrão a partir de dataclasses.

Atributos + Representação + Comparação

imp dataclasses


@dataclasses.dataclass
#@dataclasses.dataclass()                                       # alternative
class Color:
    r : int = 0
    g : int = 0
    b : int = 0

Os seguintes valores por omissão estão definidos:

@dataclasses.dataclass(init=True, repr=True, eq=True)
O que podes ligar

Algumas funcionalidades adicionais estão disponíveis se a palavra-chave apropriada for True.

ordem

@dataclasses.dataclass(order=True)
class Color:
    r : int = 0
    g : int = 0
    b : int = 0

Os métodos de encomenda são implementados (sobrecarga < > <= >=), de forma semelhante à functools.total_ordering com testes de igualdade mais fortes.

Hashable, Mutable

@dataclasses.dataclass(unsafe_hash=True)                        # override base `__hash__`
class Color:
    ...

Embora o objecto seja potencialmente mutável (possivelmente indesejável), um hash é implementado.

Hashable, Imutável

@dataclasses.dataclass(frozen=True)                                 # `eq=True` (default) to be immutable 
class Color:
    ...

Um hash é implementado e mudar o objecto ou atribuir atributos é proibido.

Globalmente, se unsafe_hash=True ou frozen=True for definido, o objecto é hashable.

Ver também a tabela lógica original sobre detalhes.

O que não percebes

Para obter as seguintes características, os métodos especiais devem ser manualmente implementado:

Impacável

@dataclasses.dataclass
class Color:
    r : int = 0
    g : int = 0
    b : int = 0

    def __iter__(self):
        yield from dataclasses.astuple(self)

optimização

@dataclasses.dataclass
class SlottedColor:
    __slots__ = ["r", "b", "g"]
    r : int
    g : int
    b : int

O tamanho do objecto está agora reduzido:

>>> imp sys
>>> sys.getsizeof(Color)
1056
>>> sys.getsizeof(SlottedColor)
888

Em algumas circunstâncias, {[19] } também melhora a velocidade de criação de instância e acesso de atributos. Além disso, os 'slots' não permitem atribuições predefinidas; caso contrário, um ValueError é elevado.

Veja mais em slots neste postno blog .


Tabela-Resumo

+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
|       Feature        |       Keyword        |                      Example                       |           Implement in a Class          |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
| Attributes           |  init                |  Color().r -> 0                                    |  __init__                               |
| Representation       |  repr                |  Color() -> Color(r=0, g=0, b=0)                   |  __repr__                               |
| Comparision*         |  eq                  |  Color() == Color(0, 0, 0) -> True                 |  __eq__                                 |
|                      |                      |                                                    |                                         |
| Order                |  order               |  sorted([Color(0, 50, 0), Color()]) -> ...         |  __lt__, __le__, __gt__, __ge__         |
| Hashing              |  unsafe_hash/frozen  |  {Color(), {Color()}} -> {Color(r=0, g=0, b=0)}    |  __hash__                               |
| Immutable            |  frozen + eq         |  Color().r = 10 -> TypeError                       |  __setattr__, __delattr__               |
|                      |                      |                                                    |                                         |
| Unpackable+          |  -                   |  r, g, b = Color()                                 |   __iter__                              |
| Optimization+        |  -                   |  sys.getsizeof(SlottedColor) -> 888                |  __slots__                              |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+

+estes métodos não são gerados automaticamente e requerem implementação manual em um dataclass.

*__ne__ não está implementado.


Características adicionais

Pós-inicialização

@dataclasses.dataclass
class RGBA:
    r : int = 0
    g : int = 0
    b : int = 0
    a : float = 1.0

    def __post_init__(self):
        self.a : int =  int(self.a * 255)


RGBA(127, 0, 255, 0.5)
# RGBA(r=127, g=0, b=255, a=127)

Herança

@dataclasses.dataclass
class RGBA(Color):
    a : int = 0

Conversões

Converta um dataclass para uma tupla ou uma dict, recursivamente:

>>> dataclasses.astuple(Color(128, 0, 255))
(128, 0, 255)
>>> dataclasses.asdict(Color(128, 0, 255))
{r: 128, g: 0, b: 255}

Limitações

    Falta de mecanismos para lidar com argumentos de starry

Referências

  • R. Hettinger's talk on Dataclasses: the code generator to end all code generators
  • T. Hunner's talk on classes mais fáceis: classes Python sem todo o Cruft
  • Documentação De Python sobre os detalhes da lavagem
  • O guia real do Python em O Guia final das Classes de dados em Python 3.7
  • [[32]}A. Shaw's post no blog on a brief tour of Python 3.7 data classes
  • o repositório de github de E. Smith em dataclasses
 1
Author: pylang, 2018-09-11 19:57:08