Menos código repetitivo para classes que guardam dados
Muitas classes existem principalmente para guardar alguns campos: uma operação, uma cotação, uma configuração. Escrever __init__, __repr__ e __eq__ para cada uma é repetitivo. O decorador @dataclass do módulo dataclasses os escreve para você a partir de uma lista de campos anotados. Você declara symbol: str, price: float, quantity: int uma vez e obtém um construtor Trade("AAPL", 184.25, 200), um repr legível como Trade(symbol='AAPL', price=184.25, quantity=200) e igualdade campo a campo. As anotações documentam a intenção; o Python não as impõe.
from dataclasses import dataclass
@dataclass
class Trade:
symbol: str
price: float
quantity: int
a = Trade("AAPL", 184.25, 200)
b = Trade("AAPL", 184.25, 200)
print(a) # Trade(symbol='AAPL', price=184.25, quantity=200)
print(a == b) # True: compared field by field
print(a.price * a.quantity) # 36850.0Valores padrão, field() e valores mutáveis
Um campo pode ter um valor padrão (currency: str = "USD"), e os campos com padrão devem vir depois dos que não têm. Para um padrão mutável como uma lista, um simples = [] seria compartilhado por todos os objetos (e o Python recusa); escreva trades: list = field(default_factory=list) para que cada objeto receba sua própria lista nova. field(...) também permite ocultar um campo do repr (repr=False) ou das comparações (compare=False).
from dataclasses import dataclass, field
@dataclass
class Book:
name: str
currency: str = "USD" # a default
trades: list = field(default_factory=list) # a fresh list per object
one = Book("desk-a")
two = Book("desk-b")
one.trades.append(("AAPL", 100))
print(one) # Book(name='desk-a', currency='USD', trades=[('AAPL', 100)])
print(two.trades) # [] (not shared)
print(Book("x", "EUR")) # Book(name='x', currency='EUR', trades=[])Seus próprios métodos, __post_init__ e construtores alternativos
Uma dataclass continua sendo uma classe normal, então você pode adicionar métodos. __post_init__(self) roda logo após o __init__ gerado, o lugar para validar ou limpar valores (self.symbol = self.symbol.upper(), raise ValueError(...) para um preço ruim). Um @classmethod recebe a classe como cls e é a forma usual de construir um objeto a partir de outra forma, como texto: Trade.from_line("AAPL,184.25,200") devolve cls(symbol, float(price), int(quantity)), assim a validação continua valendo e as subclasses também funcionam.
from dataclasses import dataclass
@dataclass
class Trade:
symbol: str
price: float
quantity: int
def __post_init__(self):
self.symbol = self.symbol.strip().upper()
if self.price <= 0:
raise ValueError("price must be positive")
def notional(self):
return self.price * abs(self.quantity)
@classmethod
def from_line(cls, line):
symbol, price, quantity = line.split(",")
return cls(symbol, float(price), int(quantity))
t = Trade.from_line(" aapl,184.25,-200")
print(t) # Trade(symbol='AAPL', price=184.25, quantity=-200)
print(t.notional()) # 36850.0Congeladas, ordenadas e quando as classes comuns vencem
@dataclass(frozen=True) torna as instâncias somente leitura (atribuir a um campo gera FrozenInstanceError) e lhes dá um hash, então podem ser chaves de dicionário ou membros de conjunto: ideal para valores que não devem mudar, como a especificação de um contrato. @dataclass(order=True) adiciona <, <=, >, >= comparando os campos em ordem. Use uma dataclass para registros de dados; para objetos com muito comportamento e estado oculto, uma classe escrita à mão com métodos cuidadosos é mais clara.
from dataclasses import dataclass
@dataclass(frozen=True, order=True)
class Contract:
symbol: str
expiry: str # ISO dates sort correctly as text
a = Contract("ESZ5", "2025-12-19")
b = Contract("ESZ5", "2026-03-20")
print(a < b) # True
print({a: "front", b: "next"}[a]) # front (frozen => hashable)
try:
a.symbol = "NQZ5"
except Exception as error:
print(type(error).__name__) # FrozenInstanceErrorprice: float não impedem que Trade("AAPL", "abc", 1) seja criado. Se valores ruins corromperiam em silêncio cálculos posteriores, verifique-os em __post_init__ e gere ValueError, para que um registro quebrado não possa existir. Crie sempre os objetos pelo construtor normal, inclusive a partir de from_line, para que a verificação não possa ser ignorada.@dataclasstransforma campos anotados em__init__,__repr__e__eq__.- Os padrões vêm por último; use
field(default_factory=list)para listas e dicionários. __post_init__valida;@classmethodconstrói a partir de outras formas;frozen=Truetorna os valores somente leitura.
A seguir: derivadas em código, estimar a rapidez com que uma grandeza muda, a ideia por trás das sensibilidades de opções como delta e gama.
Lab: o que é avaliado
- Limpar e validar uma operação com
__post_init__25 pts - Calcular o valor negociado com
notional25 pts - Construir uma operação a partir de texto com
from_line25 pts - Calcular o saldo das operações de um símbolo com
net_quantity25 pts