Où vit un nom
La portée d'un nom est la partie du programme où il peut être utilisé. Un nom créé dans une fonction est local : il n'existe que pendant l'exécution de cette fonction et est invisible à l'extérieur. Un nom créé au niveau supérieur du fichier est global : les fonctions peuvent le lire. Quand Python rencontre un nom, il cherche d'abord dans la fonction, puis dans toute fonction englobante, puis dans le fichier, puis dans les noms prédéfinis.
rate = 0.001 # global: defined outside any function
def fee(notional):
minimum = 1.0 # local: exists only during the call
return max(minimum, notional * rate)
print(fee(36850.0)) # 36.85
# print(minimum) # would raise NameError: minimum is not definedAffecter crée un nom local
Lire une variable globale depuis une fonction ne pose pas de problème. Mais affecter un nom dans une fonction crée un nouveau nom local, même s'il existe un global du même nom : le global reste intact. Cela protège vos données, et c'est aussi pourquoi modifier un état global depuis une fonction demande un mot de plus (global) dont vous devriez rarement avoir besoin. Passez les valeurs en arguments et renvoyez les résultats avec return.
# Assigning inside a function makes a new local name
balance = 1000
def deposit(amount):
balance = amount # a different, local balance
return balance
print(deposit(50)) # 50
print(balance) # 1000 (the global one is unchanged)Des fonctions qui fabriquent des fonctions
Une fonction peut définir une autre fonction à l'intérieur d'elle-même et la renvoyer avec return. La fonction interne garde l'accès aux variables de la fonction externe, même après la fin de celle-ci. Cet ensemble formé d'une fonction et des valeurs qu'elle retient est une fermeture (closure). C'est une façon élégante de fabriquer un outil à partir de réglages : un calculateur pour les actions, un autre pour les ETF.
# A closure remembers the variables of the function that made it
def make_fee_calculator(rate, minimum):
def fee(notional):
return max(minimum, notional * rate)
return fee
stock_fee = make_fee_calculator(0.001, 1.0)
etf_fee = make_fee_calculator(0.0005, 0.5)
print(stock_fee(36850.0)) # 36.85
print(etf_fee(36850.0)) # 18.425Un état qui vit entre les appels : nonlocal
Une fermeture peut aussi modifier ce qu'elle retient. Comme une affectation créerait un nouveau nom local, la fonction interne déclare d'abord nonlocal count : « ce nom appartient à la fonction englobante ». Chaque appel de la fonction externe crée sa propre mémoire, de sorte que deux compteurs n'interfèrent jamais. C'est ainsi qu'un générateur d'identifiants d'ordres ou une moyenne glissante garde son état sans variable globale.
# nonlocal lets the inner function rebind the outer variable
def make_counter():
count = 0
def next_id():
nonlocal count
count += 1
return count
return next_id
order_ids = make_counter()
print(order_ids(), order_ids(), order_ids()) # 1 2 3
other = make_counter()
print(other()) # 1 (its own count)# State between calls: an exponential moving average
def make_ema(alpha):
value = None
def update(price):
nonlocal value
value = price if value is None else alpha * price + (1 - alpha) * value
return value
return update
ema = make_ema(0.5)
print(ema(100.0), ema(110.0), ema(110.0)) # 100.0 105.0 107.5- Dans une fonction, les noms que vous affectez sont locaux ; passez les données en entrée, renvoyez-les avec
return. - Une fonction qui construit et renvoie une fonction transforme des réglages en outil.
- Utilisez
nonlocalpour l'état qui doit survivre entre les appels, une mémoire par fermeture.
À suivre : les compréhensions de listes, pour construire une nouvelle liste à partir d'une autre en une seule ligne lisible.
Lab : ce qui est noté
- Construire un compteur qui retient son total avec
make_counter25 pts - Emballer des réglages de frais dans une fonction avec
make_fee_calculator25 pts - Garder une moyenne mobile entre les appels avec
make_ema25 pts - Créer un contrôle de prix à partir d'une limite avec
make_limit_checker25 pts