Visão geral
A questão foi abordada. No entanto, esta resposta adiciona alguns exemplos práticos para ajudar no entendimento básico de classes de dados.
O que exatamente são as classes de dados python e quando é melhor usá-las?
- geradores de código : gerar código padrão; você pode optar por implementar métodos especiais em uma classe regular ou ter uma classe de dados implementá-los automaticamente.
- contêineres de dados : estruturas que mantêm dados (por exemplo, tuplas e dictos), geralmente com pontos, atribuem acesso como classes
namedtuple
e outras .
"mutuamente nomeados com padrão [s]"
Aqui está o que a última frase significa:
- mutável : por padrão, os atributos da classe de dados podem ser reatribuídos. Você pode opcionalmente torná-los imutáveis (veja os exemplos abaixo).
- namedtuple : você pontilhou, atribui o acesso como uma
namedtuple
classe ou uma classe regular.
- padrão : você pode atribuir valores padrão a atributos.
Comparado às classes comuns, você economiza principalmente na digitação do código padrão.
Recursos
Esta é uma visão geral dos recursos da classe de dados (TL; DR? Consulte a Tabela Resumo na próxima seção).
O que você ganha
Aqui estão os recursos que você obtém por padrão das classes de dados.
Atributos + Representação + Comparação
import dataclasses
@dataclasses.dataclass
#@dataclasses.dataclass() # alternative
class Color:
r : int = 0
g : int = 0
b : int = 0
Esses padrões são fornecidos configurando automaticamente as seguintes palavras-chave para True
:
@dataclasses.dataclass(init=True, repr=True, eq=True)
O que você pode ativar
Recursos adicionais estão disponíveis se as palavras-chave apropriadas estiverem definidas como True
.
Ordem
@dataclasses.dataclass(order=True)
class Color:
r : int = 0
g : int = 0
b : int = 0
Os métodos de ordenação estão agora implementados (sobrecarregando os operadores :) < > <= >=
, da mesma forma que functools.total_ordering
nos testes de igualdade mais fortes.
Hashable, Mutable
@dataclasses.dataclass(unsafe_hash=True) # override base `__hash__`
class Color:
...
Embora o objeto seja potencialmente mutável (possivelmente indesejado), um hash é implementado.
Hashable, imutável
@dataclasses.dataclass(frozen=True) # `eq=True` (default) to be immutable
class Color:
...
Um hash agora está implementado e a alteração do objeto ou a atribuição de atributos não é permitida.
No geral, o objeto é hashable se unsafe_hash=True
ou frozen=True
.
Veja também a tabela lógica de hash original com mais detalhes.
O que você não recebe
Para obter os seguintes recursos, métodos especiais devem ser implementados manualmente:
Desembalar
@dataclasses.dataclass
class Color:
r : int = 0
g : int = 0
b : int = 0
def __iter__(self):
yield from dataclasses.astuple(self)
Otimização
@dataclasses.dataclass
class SlottedColor:
__slots__ = ["r", "b", "g"]
r : int
g : int
b : int
O tamanho do objeto agora é reduzido:
>>> imp sys
>>> sys.getsizeof(Color)
1056
>>> sys.getsizeof(SlottedColor)
888
Em algumas circunstâncias, __slots__
também melhora a velocidade de criação de instâncias e acesso a atributos. Além disso, os slots não permitem atribuições padrão; caso contrário, a ValueError
é gerado.
Veja mais sobre slots nesta postagem do blog .
Tabela de 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__ |
| Hashable | unsafe_hash/frozen | {Color(), {Color()}} -> {Color(r=0, g=0, b=0)} | __hash__ |
| Immutable | frozen + eq | Color().r = 10 -> TypeError | __setattr__, __delattr__ |
| | | | |
| Unpacking+ | - | r, g, b = Color() | __iter__ |
| Optimization+ | - | sys.getsizeof(SlottedColor) -> 888 | __slots__ |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
+ Esses métodos não são gerados automaticamente e requerem implementação manual em uma classe de dados.
* __ne__
não é necessário e, portanto, não é 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 uma classe de dados em uma tupla ou um ditado, recursivamente :
>>> dataclasses.astuple(Color(128, 0, 255))
(128, 0, 255)
>>> dataclasses.asdict(Color(128, 0, 255))
{r: 128, g: 0, b: 255}
Limitações
Referências
- R. Hettinger fala sobre Dataclasses: O gerador de código para terminar todos os geradores de código
- A palestra de T. Hunner sobre Classes Mais Fáceis: Classes Python Sem Todo o Cruft
- Documentação do Python sobre detalhes de hash
- Real Python's guide on The Ultimate Guide to Data Classes in Python 3.7
- Postagem do blog de A. Shaw em Um breve tour pelas classes de dados do Python 3.7
- Repositório github de E. Smith em classes de dados
namedtuple
s são imutáveis e não podem ter valores padrão para os atributos, enquanto as classes de dados são mutáveis e podem tê-los.