Dados e comportamento juntos
Até agora uma posição podia ser uma tupla ou um dicionário, com funções separadas trabalhando sobre ela. Uma classe reúne os dados e as funções que lhes pertencem em um novo tipo. class Position: define a planta; chamá-la, Position("AAPL", 100, 180.0), constrói um objeto (também chamado instância) a partir dela. Cada objeto guarda seus próprios dados, e as funções definidas na classe, chamadas métodos, trabalham sobre esse objeto.
class Position:
def __init__(self, symbol, quantity=0):
self.symbol = symbol # attributes belong to this object
self.quantity = quantity
def describe(self):
return f"{self.quantity} x {self.symbol}"
apple = Position("AAPL", 100)
tesla = Position("TSLA")
print(apple.describe()) # 100 x AAPL
print(tesla.describe()) # 0 x TSLA
print(apple.quantity, tesla.quantity) # 100 0__init__ e self
__init__ é o construtor: o Python o executa automaticamente ao criar um objeto, e ele guarda os valores iniciais. Todo método recebe o próprio objeto como primeiro parâmetro, chamado por convenção self; você não o passa, o Python passa (apple.describe() chama Position.describe(apple)). Tudo o que é guardado com self.nome = valor é um atributo daquele objeto e permanece ali entre chamadas de método. __init__ prepara as coisas e não devolve nada.
class Account:
def __init__(self, cash):
self.cash = cash
def deposit(self, amount):
self.cash += amount # changes this object's attribute
return self.cash
first = Account(1000)
second = Account(50)
first.deposit(500)
print(first.cash, second.cash) # 1500 50 (separate objects, separate data)
same = first # a second name for the same object
same.deposit(1)
print(first.cash) # 1501Métodos que mudam o estado ou respondem perguntas
Alguns métodos respondem a uma pergunta sobre o objeto sem alterá-lo (market_value(price)); devem só devolver um resultado e deixar os atributos em paz. Outros alteram o objeto (apply_fill), atualizam atributos e muitas vezes devolvem algo útil, como o lucro realizado. Um método pode chamar outro com self.outro_metodo(...), o que mantém as regras em um só lugar: se unrealized_pnl precisa do valor de mercado, ele pergunta a self.market_value(price) em vez de repetir a fórmula.
class Position:
def __init__(self, symbol, quantity=0, avg_price=0.0):
self.symbol = symbol
self.quantity = quantity
self.avg_price = avg_price
def market_value(self, price):
return self.quantity * price
def unrealized_pnl(self, price):
return self.market_value(price) - self.quantity * self.avg_price
pos = Position("AAPL", 100, 180.0)
print(pos.market_value(185.0)) # 18500.0
print(pos.unrealized_pnl(185.0)) # 500.0Armadilhas e quando usar uma classe
Esquecer self. é o deslize clássico: quantity = 5 dentro de um método cria uma variável local descartável, não um atributo. Use CapWords para nomes de classes (Position) e minúsculas com sublinhados para atributos e métodos; um sublinhado inicial (_cache) marca algo como interno. Nunca use um padrão mutável como def __init__(self, fills=[]): todos os objetos compartilhariam aquela única lista; use fills=None e crie [] dentro. Crie uma classe quando os dados e as regras sobre eles andam juntos; uma função simples ou um dicionário costuma ser mais simples.
class Wrong:
def __init__(self, quantity):
quantity = quantity # forgot self.: nothing is stored
class Shared:
def __init__(self, fills=[]): # one list shared by every object
self.fills = fills
class Right:
def __init__(self, fills=None):
self.fills = [] if fills is None else list(fills)
a, b = Shared(), Shared()
a.fills.append(100)
print(b.fills) # [100] (surprise!)
c, d = Right(), Right()
c.fills.append(100)
print(d.fills) # []
print(hasattr(Wrong(5), "quantity")) # Falseclass Nome:define um tipo;Nome(args)constrói um objeto e executa__init__.self.attr = valorguarda dados no objeto; cada método recebeselfprimeiro.- Métodos de consulta devolvem respostas; os de atualização alteram atributos; evite padrões mutáveis.
A seguir: métodos especiais como __repr__ e __eq__, que fazem seus objetos serem impressos bem e comparados como você espera.
Lab: o que é avaliado
- Construir uma posição com
__init__25 pts - Avaliar a posição com
market_value25 pts - Medir o lucro não realizado com
unrealized_pnl25 pts - Atualizar a posição após uma operação com
apply_fill25 pts