Données et comportement réunis
Jusqu'ici, une position pouvait être un tuple ou un dictionnaire, avec des fonctions séparées qui travaillent dessus. Une classe regroupe les données et les fonctions qui leur appartiennent en un nouveau type. class Position: définit le plan ; l'appeler, Position("AAPL", 100, 180.0), construit un objet (aussi appelé instance) d'après ce plan. Chaque objet contient ses propres données, et les fonctions définies dans la classe, appelées méthodes, travaillent sur cet objet.
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__ et self
__init__ est le constructeur : Python l'exécute automatiquement quand on crée un objet, et il enregistre les valeurs de départ. Chaque méthode reçoit l'objet lui-même comme premier paramètre, appelé par convention self ; on ne le passe pas, Python s'en charge (apple.describe() appelle Position.describe(apple)). Tout ce qui est enregistré par self.nom = valeur est un attribut de cet objet et y reste entre les appels de méthode. __init__ prépare les choses et ne renvoie rien.
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) # 1501Des méthodes qui changent l'état ou répondent à des questions
Certaines méthodes répondent à une question sur l'objet sans le modifier (market_value(price)) ; elles doivent juste renvoyer un résultat et laisser les attributs tranquilles. D'autres modifient l'objet (apply_fill), mettent à jour des attributs et renvoient souvent quelque chose d'utile comme le gain réalisé. Une méthode peut en appeler une autre avec self.autre_methode(...), ce qui garde les règles à un seul endroit : si unrealized_pnl a besoin de la valeur de marché, elle demande self.market_value(price) plutôt que de répéter la formule.
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.0Pièges et quand utiliser une classe
Oublier self. est l'étourderie classique : quantity = 5 dans une méthode crée une variable locale jetable, pas un attribut. Utilisez CapWords pour les noms de classes (Position) et minuscules avec tirets bas pour attributs et méthodes ; un tiret bas initial (_cache) indique un élément interne. N'utilisez jamais une valeur par défaut modifiable comme def __init__(self, fills=[]) : tous les objets partageraient cette unique liste ; utilisez fills=None et créez [] dedans. Créez une classe quand les données et les règles qui les concernent vont ensemble ; une simple fonction ou un dictionnaire est souvent plus simple.
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 Nom:définit un type ;Nom(args)construit un objet et exécute__init__.self.attr = valeurenregistre une donnée sur l'objet ; chaque méthode prendselfen premier.- Les méthodes de requête renvoient des réponses ; celles de mise à jour modifient des attributs ; évitez les valeurs par défaut modifiables.
À suivre : les méthodes spéciales comme __repr__ et __eq__, qui font que vos objets s'affichent bien et se comparent comme prévu.
Lab : ce qui est noté
- Construire une position avec
__init__25 pts - Valoriser la position avec
market_value25 pts - Mesurer le gain latent avec
unrealized_pnl25 pts - Mettre à jour la position après une transaction avec
apply_fill25 pts