Les annotations sont une documentation qu'un outil peut vérifier
Python est typé dynamiquement : une variable peut contenir n'importe quoi, et un mauvais type n'apparaît qu'à l'exécution de la ligne, peut-être en pleine séance. Les annotations de type permettent d'écrire ce qu'une fonction attend et retourne : def mid_price(bid: float, ask: float) -> float. Python les ignore à l'exécution. Leur intérêt est qu'un éditeur et un vérificateur statique comme mypy ou pyright les lisent et vous avertissent d'un mauvais appel avant même l'exécution, et qu'elles documentent la fonction mieux qu'un commentaire qui vieillit.
# Hints describe the function; Python does not enforce them when it runs
def mid_price(bid: float, ask: float) -> float:
return (bid + ask) / 2
print(mid_price(100.0, 100.5)) # 100.25
print(mid_price.__annotations__) # {'bid': <class 'float'>, 'ask': <class 'float'>, 'return': <class 'float'>}
print(mid_price(10, 12)) # 11.0 an int is accepted where float is hinted
try:
mid_price("a", "b") # a checker flags this line before the code runs
except TypeError as error:
print("only fails when it runs:", error) # only fails when it runs: unsupported operand type(s) for /Conteneurs, None et unions
Les conteneurs natifs prennent le type de leurs éléments entre crochets : list[float], dict[str, float], tuple[str, float] (un type par position). Une valeur qui peut manquer s'écrit float | None, ce qui oblige l'appelant à gérer None ; le vérificateur se plaint s'il emploie le résultat comme un nombre sans l'avoir testé. Dans la fonction, un if x is None: return restreint le type, et la suite sait que c'est un float. Pour les arguments simplement lus, préférez un type général comme Sequence[float] (listes, tuples) à list[float], pour que plus d'appelants conviennent ; retournez un type concret, comme list[float].
# Containers, an optional result, and narrowing with an is None check
def best_quote(quotes: list[tuple[str, float]]) -> tuple[str, float] | None:
if not quotes:
return None
return min(quotes, key=lambda quote: quote[1])
def describe(quotes: list[tuple[str, float]]) -> str:
best = best_quote(quotes)
if best is None: # after this check the checker knows best is a tuple
return "no quotes"
venue, price = best
return f"{venue} at {price:.2f}"
print(describe([("XNAS", 100.02), ("ARCX", 100.01)])) # ARCX at 100.01
print(describe([])) # no quotesFormes de données, et le vérificateur au travail
Un dictionnaire à clés fixes, comme un ordre, vaut mieux décrit que laissé en dict. Un TypedDict ou une dataclass nomme chaque champ et son type : une faute comme order["quantiy"] devient une erreur dans l'éditeur au lieu d'une KeyError à 15 h 59. On lance le vérificateur en ligne de commande (mypy main.py) ; il affiche chaque endroit où le code contredit ses annotations et sort avec un code non nul, ce qui s'insère dans les contrôles automatisés que vous verrez plus tard. On peut ajouter les annotations progressivement : Any désactive la vérification d'une valeur, et le code non typé est simplement ignoré. Les annotations sont aussi des données, que des outils lisent à l'exécution avec typing.get_type_hints, exactement comme ce labo les vérifie.
# A TypedDict names the fields of an order; the checker would flag order["quantiy"]
from typing import TypedDict
class Order(TypedDict):
symbol: str
quantity: int
price: float
def notional(order: Order) -> float:
return order["quantity"] * order["price"]
order: Order = {"symbol": "AAPL", "quantity": 100, "price": 184.5}
print(notional(order)) # 18450.0
print(Order.__annotations__) # {'symbol': <class 'str'>, 'quantity': <class 'int'>, 'price': <class 'float'>}# The hints can be read at run time, which is how tools (and this lab) inspect a function
import typing
def apply_fee(prices: typing.Sequence[float], fee_rate: float = 0.001) -> list[float]:
return [price * (1 + fee_rate) for price in prices]
hints = typing.get_type_hints(apply_fee)
print(hints["fee_rate"], hints["return"]) # <class 'float'> list[float]
print(typing.get_origin(hints["prices"]).__name__) # Sequence
print(apply_fee((100.0, 200.0))) # a tuple is accepted: [100.1, 200.2]- Les annotations (
bid: float,-> float) documentent une fonction et sont ignorées à l'exécution ; un vérificateur comme mypy les lit. - Les conteneurs prennent des types d'éléments (
list[tuple[str, float]]) ; une valeur absente estfloat | None; les arguments en lecture seule peuvent être unSequence[float]général. - Utilisez
TypedDictoudataclasspour les enregistrements, lancez le vérificateur dans vos contrôles automatisés, et validez toujours les données externes et testez les nombres.
À suivre : les tests avec pytest, fixtures, cas paramétrés et les contrôles qui gardent une stratégie honnête.
Lab : ce qui est noté
- Annoter des nombres avec
mid_price25 pts - Typer un conteneur avec
best_quote25 pts - Admettre une valeur absente dans
safe_ratio25 pts - Accepter n'importe quelle séquence dans
apply_fee25 pts