Datos y comportamiento juntos
Hasta ahora una posición podía ser una tupla o un diccionario, con funciones separadas que trabajan sobre ella. Una clase agrupa los datos y las funciones que les pertenecen en un tipo nuevo. class Position: define el plano; llamarla, Position("AAPL", 100, 180.0), construye un objeto (también llamado instancia) a partir de ese plano. Cada objeto guarda sus propios datos, y las funciones definidas en la clase, llamadas métodos, trabajan sobre ese 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__ y self
__init__ es el constructor: Python lo ejecuta automáticamente al crear un objeto y guarda los valores iniciales. Cada método recibe el propio objeto como primer parámetro, llamado por convención self; no lo pasas tú, lo pasa Python (apple.describe() llama a Position.describe(apple)). Lo que se guarda con self.nombre = valor es un atributo de ese objeto y permanece ahí entre llamadas a métodos. __init__ prepara las cosas y no devuelve 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 cambian el estado o responden preguntas
Algunos métodos responden una pregunta sobre el objeto sin cambiarlo (market_value(price)); solo deben devolver un resultado y dejar los atributos en paz. Otros cambian el objeto (apply_fill), actualizan atributos y a menudo devuelven algo útil como la ganancia realizada. Un método puede llamar a otro con self.otro_metodo(...), lo que mantiene las reglas en un solo lugar: si unrealized_pnl necesita el valor de mercado, pregunta a self.market_value(price) en vez de repetir la 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.0Trampas y cuándo usar una clase
Olvidar self. es el descuido clásico: quantity = 5 dentro de un método crea una variable local desechable, no un atributo. Usa CapWords para los nombres de clase (Position) y minúsculas con guiones bajos para atributos y métodos; un guion bajo inicial (_cache) marca algo como interno. Nunca uses un valor por defecto mutable como def __init__(self, fills=[]): todos los objetos compartirían esa única lista; usa fills=None y crea [] dentro. Crea una clase cuando los datos y las reglas sobre ellos viajan juntos; una función simple o un diccionario suele ser más sencillo.
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 Nombre:define un tipo;Nombre(args)construye un objeto y ejecuta__init__.self.attr = valorguarda datos en el objeto; cada método recibeselfprimero.- Los métodos de consulta devuelven respuestas; los de actualización cambian atributos; evita los valores por defecto mutables.
A continuación: métodos especiales como __repr__ y __eq__, que hacen que tus objetos se impriman bien y se comparen como esperas.
Lab: qué se califica
- Construir una posición con
__init__25 pts - Valorar la posición con
market_value25 pts - Medir la ganancia latente con
unrealized_pnl25 pts - Actualizar la posición tras una operación con
apply_fill25 pts