Commit 6e27c409 authored by Matthieu Boileau's avatar Matthieu Boileau
Browse files

Up 03

parent 8eab4337
%% Cell type:markdown id: tags:
# Opérations, contrôle, fonctions et modules
![](fig/python-logo.png)
- Opérateurs
- Structures de contrôle
- Fonctions
- Exceptions & gestionnaires de contexte
- Compréhensions de listes & expressions génératrices
- Modules
- Bonnes pratiques
***
*Contenu sous licence [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0)*
%% Cell type:markdown id: tags:
## Opérateurs
### Arithmétiques
```python
+, -, *, /, //, %, **
```
Les classiques se comportent "normalement", pas besoin d'entrer dans les détails.
%% Cell type:markdown id: tags:
#### Particularités de la division
%% Cell type:code id: tags:
``` python
# Avec des nombres entiers
print(16 / 3) # Quotient de la division euclidienne (produit un réel)
print(16 // 3) # Quotient de la division euclidienne (produit un entier)
print(16 % 3) # Reste de la division euclidienne (produit un entier)
# Avec des nombres flottants
print(16. / 3) # Division (produit un réel)
print(16. // 3) # Quotient de la division (produit un réel)
print(16. % 3) # Reste de la division ou modulo (produit un réel)
```
%% Cell type:markdown id: tags:
#### Puissance
%% Cell type:code id: tags:
``` python
print(2 ** 10)
print(2**10)
# On peut aussi utiliser la fonction pow() du module math, mais celle-ci renvoie un réel...
import math
print(math.pow(2, 10))
```
%% Cell type:markdown id: tags:
### Logiques
```python
and, or, not
```
retournent une valeur booléenne.
%% Cell type:code id: tags:
``` python
print(True or False)
print(True and False)
print(not True)
print(not False)
print(not [])
print(not (1, 2, 3))
```
%% Cell type:markdown id: tags:
Attention, ce sont des opérateurs "court-circuit" :
%% Cell type:code id: tags:
``` python
a = True
b = False and a # b vaut False sans que a soit évalué
c = True or a # c vaut True, sans que a soit évalué
```
%% Cell type:markdown id: tags:
Pour s'en convaincre :
%% Cell type:code id: tags:
``` python
True or print("nicht a kurz schluss")
False and print("not a short circuit")
print('on a prouvé que ce sont des opérateurs "court-circuit"...')
```
%% Cell type:markdown id: tags:
> **Exercice :** Modifiez les valeurs `True` et `False` dans la cellule précédente, pour visualiser le fonctionnement de ces opérateurs.
%% Cell type:markdown id: tags:
### Comparaison
```python
==, is, !=, is not, >, >=, <, <=
```
L'évaluation de ces opérateurs retourne une valeur booléenne.
%% Cell type:code id: tags:
``` python
print(2 == 2)
print(2 != 2)
print(2 == 2.0)
print(type(2) is int)
```
%% Cell type:markdown id: tags:
On peut utiliser ces opérateurs avec des variables et des appels à des fonctions.
%% Cell type:code id: tags:
``` python
x = 3
print(1 > x)
y = [0, 1, 42, 0]
print(x <= max(y))
print(x <= min(y))
```
%% Cell type:markdown id: tags:
On peut chaîner ces opérateurs, mais ils fonctionnent en mode "court-circuit" et l'opérande centrale n'est évaluée qu'une seule fois.
%% Cell type:code id: tags:
``` python
x = 3
print(2 < x <= 9) # équivalent à 2 < x and x <= 9
```
%% Cell type:markdown id: tags:
**Attention :** comparer des types **non numériques** peut avoir des résultats surprenants.
%% Cell type:code id: tags:
``` python
# Chaînes de caractères
print("aaa" < "abc")
print("aaa" < "aaaa")
print("22" > "3.0")
```
%% Cell type:code id: tags:
``` python
# Listes
print([1, 2, 3, 4] > [42, 42])
print([666] > [42, 42])
```
%% Cell type:markdown id: tags:
**Attention :** comparer des types **incompatibles** peut avoir des résultats surprenants.
%% Cell type:code id: tags:
``` python
# Cette cellule génère des erreurs
print('chaîne:\t', "a" < 2)
print('liste:\t', ["zogzog"] > 42)
print('vide:\t', [] > 1)
print('tuple:\t', [23, 24] >= (23, 24))
print('dict:\t', [23, 24] >= {23: True, 24: "c'est pas faux"})
```
%% Cell type:markdown id: tags:
Attention, l'égalité de valeur n'implique pas forcément que l'identité des objets comparés est la même.
%% Cell type:code id: tags:
``` python
a = [] # a est une liste vide
b = [] # b est une AUTRE liste vide
print(a == b) # test de valeur
print(a is b) # test d'identité
print('id(a) =', id(a))
print('id(b) =', id(b))
```
%% Cell type:markdown id: tags:
Mais, comme vu précédemment, des variables différentes peuvent référencer le même objet.
%% Cell type:code id: tags:
``` python
c = a
print(a == c) # test de valeur
print(a is c) # test d'identité
```
%% Cell type:markdown id: tags:
### bits à bits *(bitwise)*
```python
|, ^, &, <<, >>, ~
```
- Ces opérateurs permettent de manipuler individuellement les bits d'un entier.
- Ce sont des opérations bas-niveau, souvent utilisées pour piloter directement du matériel, pour implémenter des protocoles de communication binaires (par exemple réseau ou disque).
- L'utilisation d'entiers comme ensemble de bits permet des encodages de données très compacts, un booléen (True, False) ne prendrait qu'un bit en mémoire, c'est a dire que l'on peut encoder 64 booléens dans un entier.
Description complète [ici](https://wiki.python.org/moin/BitwiseOperators).
%% Cell type:code id: tags:
``` python
val = 67 # == 64 + 2 + 1 == 2**6 + 2**1 + 2**0 == 0b1000011
print(bin(val))
mask = 1 << 0 # On veut récupérer le 1er bit
print('le 1er bit vaut', (val & mask) >> 0)
mask = 1 << 1 # On veut récupérer le 2ème bit
print('le 2ème bit vaut', (val & mask) >> 1)
mask = 1 << 2 # On veut récupérer le 3ème bit
print('le 3ème bit vaut', (val & mask) >> 2)
mask = 1 << 6 # On veut récupérer le 7ème bit
print('le 7ème bit vaut', (val & mask) >> 6)
# Si on positionne le 4ème bit a 1 (on rajoute 2**3 = 8)
newval = val | (1 << 3)
print(newval)
# Si on positionne le 6ème bit a 0 (on soustrait 2**7 = 64)
print(newval & ~(1 << 6))
```
%% Cell type:markdown id: tags:
> **Exercice :** Retournez une chaîne de caractères représentant le nombre contenu dans `x` écrit en notation binaire.
> Par exemple :
$$
5 \rightarrow 101 \\
6 \rightarrow 110 \\
7 \rightarrow 111
$$
%% Cell type:code id: tags:
``` python
x = 7
# votre code ici
```
%% Cell type:code id: tags:
``` python
# Solution simple :
x = 7
print((x >> 0) & 1, (x >> 1) & 1, (x >> 2) & 1)
# Solution réutilisable :
def num2bin(num):
ret = []
while num > 0:
ret.append(str(num & 1))
num >>= 1
return ''.join(reversed(ret))
print(num2bin(6))
print(num2bin(7))
print(num2bin(8))
print(num2bin(42))
```
%% Cell type:markdown id: tags:
### Affectation augmentée
```python
+=, -=, *=, /=, **= # etc.
```
%% Cell type:code id: tags:
``` python
a = 4
a += 1 # <=> a = a + 1
print(a)
a //= 2
print(a)
a **= 3
print(a)
a %= 2
print(a)
```
%% Cell type:markdown id: tags:
### Compatibilité de type, coercition de type
Python effectue certaines conversions implicites quand on ne perd pas d'information (par exemple d'entier vers flottant).
%% Cell type:code id: tags:
``` python
print(1 + 0b1)
print(1 + 1.0)
print(1.0 + 2 + 0b11 + 4j)
```
%% Cell type:markdown id: tags:
Mais dans d'autres cas, la conversion doit être explicite.
%% Cell type:code id: tags:
``` python
# Cette cellule génère une erreur
a = 1
b = '1'
a + b
```
%% Cell type:markdown id: tags:
**Exercice** :
Sans toucher au `+`, corrigez la ligne `4` de la celulle ci-dessus afin d'afficher :
1. la chaîne `'11'`
2. l'entier `2`
3. l'entier `11`
%% Cell type:code id: tags:
``` python
# Solutions
# 1.
print(repr(str(a) + b)) # On utilise la fonction str()
# 2.
print(a + int(b)) # On convertit en entier
# 3.
print(int(str(a) + b)) # On fait les deux
```
%% Cell type:markdown id: tags:
### Priorité des opérateurs
Les opérateurs ont des priorités classiques en python.
Par exemple, dans l'ordre :
1. puissance : `**`
2. multiplication, division : `*` et `/`
3. addition, soustraction : `+` et `-`
etc.
- Utilisez des parenthèses quand cela aide à la lisibilité et à la clarté.
- Une priorité explicitée avec des parenthèses est souvent plus facile à relire que s'il n'y en a pas et qu'il faut se remémorer les règles.
Pour plus d'informations, voir [ici](https://docs.python.org/3/reference/expressions.html).
%% Cell type:markdown id: tags:
## Structures de contrôle
- La mise en page comme syntaxe
- `pass`
- tests conditionels : `if/elif/else`
- boucles
* `for <element> in <iterable>`
* `while`
* `break`
* `continue`
%% Cell type:markdown id: tags:
### La mise en page comme syntaxe
- La mise en page est importante en python, c'est une différence majeure avec les autres langages (Java, C/C++, etc.)
- Python utilise l'indentation du code avec des caractères blancs plutôt que des mots clés (`begin`/`end` en pascal) ou des symboles (`{}` en java et C/C++). Cela permet de rendre le code plus compact.
- Elle va servir à délimiter des blocs de code sur lesquels les structures de contrôle comme les boucles ou les tests de conditions vont s'appliquer.
- De toute façon, dans les autres langages, on indente aussi le code pour l'aspect visuel et la lisibilité.
- L'indentation faisant partie de la syntaxe du langage, il faut être rigoureux et suivre la règle suivante :
- 4 espaces pour passer au niveau suivant
- éviter les tabulations et préférer les espaces et surtout ne pas les mélanger !
%% Cell type:code id: tags:
``` python
if True:
print("toutes")
print("les")
print("lignes")
print("au même niveau d'indentation forment un bloc de code")
print('et quand on remonte, on "termine" un bloc de code')
```
%% Cell type:markdown id: tags:
> **Exercice** : changez le True en False, et observez quelles lignes de code ne sont plus exécutées.
%% Cell type:markdown id: tags:
### `pass`
En cas de besoin d'un bloc de code qui ne fait rien, on utilise le mot clé ``pass`` (équivalent à NOP ou NO-OP)
%% Cell type:markdown id: tags:
#### Exemple : une boucle infinie
```python
while True:
pass
```
> (à ne pas exécuter dans une cellule sous peine de bloquer le noyau de ce notebook)
%% Cell type:markdown id: tags:
### Tests conditionnels
Les instructions `if/elif/else` permettent d'exécuter des blocs d'instructions en fonction de conditions :
```python
if test1:
<bloc d instructions 1>
elif test2:
<bloc d instructions 2>
else:
<bloc d instructions 3>
```
`elif` est la contraction de "`else if`".
%% Cell type:code id: tags:
``` python
if True:
print("c'est vrai!")
```
%% Cell type:code id: tags:
``` python
if False:
print("je suis caché!")
else:
print("mais moi je suis en pleine lumière...")
```
%% Cell type:code id: tags:
``` python
# Pour cet exemple, on itère sur les éléments d'un tuple (cf. boucle for plus loin)
for position in 2, 9, 3, 1, 8:
if position == 1:
print(position, "Or")
elif position == 2:
print(position, "Argent")
elif position == 3:
print(position, "Bronze")
else:
print(position, "Vestiaires")
```
%% Cell type:code id: tags:
``` python
taille = 1.2
if taille >= 1.70: # La taille moyenne en France
print('grand')
else:
print('petit')
```
%% Cell type:markdown id: tags:
> **Exercices** :
>
> 1. Editez la cellule pour y mettre votre taille et exécutez-la pour savoir si vous êtes grand ou petit.
> 2. Gérez le cas des gens de taille moyenne.
%% Cell type:code id: tags:
``` python
# Solution
taille = 1.54
if taille >= 2.0:
print('grand')
elif taille >= 1.70: # La taille moyenne en France
print('moyen')
else:
print('petit')
```
%% Cell type:markdown id: tags:
## Boucles
Les boucles sont les structures de contrôle permettant de répéter l'exécution d'un bloc de code plusieurs fois.
%% Cell type:markdown id: tags:
### Boucle `while`
La plus simple est la boucle de type `while` :
```python
while <condition>:
<bloc 1>
<bloc 2>
```
Tant que `<condition>` est True, le `<bloc 1>` est exécuté, quand la condition passe à False, l'exécution continue au `<bloc 2>`.
%% Cell type:code id: tags:
``` python
compteur = 3
while compteur > 0:
print('le compteur vaut :', compteur)
compteur -= 1
print('le compteur a été décrémenté 3 fois et vaut maintenant', compteur)
```
%% Cell type:markdown id: tags:
> **Exercice** :
> C'est la soirée du réveillon. Ecrivez une boucle `while` qui décompte les secondes à partir de `5` pour souhaiter la bonne année.
> Allez voir [par ici](https://docs.python.org/3/library/time.html#time.sleep) pour passer le temps...
%% Cell type:code id: tags:
``` python
import time
# Votre code ici
```
%% Cell type:code id: tags:
``` python
# Solution
import time
secs = 5
while secs > 0:
print(secs)
secs -= 1
time.sleep(1)
print('Happy new year !')
```
%% Cell type:markdown id: tags:
### Boucle `for`
Une boucle plus complexe : `for/in`
```python
for <variable> in <iterable>:
<bloc 1>
<bloc 2>
```
A chaque tour de boucle, la variable `<variable>` va référencer un des éléménts de l'`<iterable>`. La boucle s'arrête quand tous les éléménts de l'itérable ont été traités. Il est fortement déconseillé de modifier l'itérable en question dans le `<bloc 1>`.
%% Cell type:code id: tags:
``` python
invites = ('Aline', 'Bernard', 'Céline', 'Dédé')
for invite in invites:
print(f'Bonjour {invite}, bienvenue à la soirée de gala !')
print('Maintenant, tout le monde a été bien accueilli.')
```
%% Cell type:markdown id: tags:
> **Exercice** : Rajoutez des invités à la fête. Vérifiez que tout le monde est accueilli correctement.
%% Cell type:code id: tags:
``` python
# Maintenant, si nous avons reçu des réponses à notre invitation, utilisons un dictionnaire :
invites = {'Aline': True, 'Bernard': False, 'Céline': True, 'Dédé': True}
for (personne, presence) in invites.items():
if presence:
print(f'Bonjour {personne}, bienvenue à la soirée de gala !')
else:
print(f'Malheureusement, {personne} ne sera pas avec nous ce soir.')
print('Maintenant, tout le monde a été bien accueilli ou excusé.')
```
%% Cell type:markdown id: tags:
La méthode `.items()` renvoie une vue itérable des éléments du dictionnaire.
%% Cell type:markdown id: tags:
> **Exercice** : Rajoutez des invités à la fête. Certains ayant répondu qu'ils ne pourraient pas venir. Vérifiez que tout le monde est accueilli ou excusé correctement.
%% Cell type:markdown id: tags:
Si on souhaite itérer sur une liste et que l'on a besoin de l'indice de ses éléments, on utilise une combinaison de [`range()`](https://docs.python.org/3/library/functions.html#func-range) et [`len()`](https://docs.python.org/3/library/functions.html#len).
%% Cell type:code id: tags:
``` python
nombres = [2, 4, 8, 6, 8, 1, 0, 1j]
for i in range(len(nombres)):
nombres[i] **= 2
print("valeurs de i:", list(range(len(nombres))))
print("carrés :", nombres)
```
%% Cell type:markdown id: tags:
Il existe une forme raccourcie pour faire ce genre de choses, la fonction interne [``enumerate()``](https://docs.python.org/3/library/functions.html#enumerate)
%% Cell type:code id: tags:
``` python
nombres = [2, 4, 8, 6, 8, 1, 0]
impairs = nombres[:] # On copie la liste nombres
for (i, nombre) in enumerate(nombres):
impairs[i] = bool(nombre % 2)
# Les impairs
print(impairs)
```
%% Cell type:markdown id: tags:
#### Note
La fonction interne [`range()`](https://docs.python.org/3/library/functions.html#func-range) retourne un itérateur. C'est un objet qui se comporte comme une liste sans pour autant allouer la mémoire nécessaire au stockage de tous ses éléments. Le coût de création d'une vraie liste augmente avec sa taille (son empreinte mémoire aussi !).
- une liste (ou un tuple) contient des données
- un itérateur possède la méthode qui permet de calculer l'élément suivant dans une boucle `for`.
#### Note de la note
En Python version 2.x, Il existait deux versions de cette fonctionnalité: `range()` et `xrange()`. La première retournait une vraie liste, allouée complètement, alors que `xrange()` retournait un itérateur.
%% Cell type:code id: tags:
``` python
print(type(range(3))) # comme xrange() en python2
print(repr(range(3))) # comme xrange() en python2
print(type(list(range(3)))) # comme range en python2
```
%% Cell type:markdown id: tags:
### Instruction `break`
Il est possible d'arrêter prématurément une boucle grâce a l'instruction `break`.
L'instruction `break` est utilisable indifférement dans les boucles `for` ou `while`.
%% Cell type:code id: tags:
``` python
compteur = 3
while True: # Notre boucle infinie
compteur -= 1
print('Dans la boucle infinie! compteur =', compteur)
if compteur <= 0:
break # On sort de la boucle while immédiatement
print('on contine, compteur =', compteur)
print("c'était pas vraiment une boucle infinie...")
```
%% Cell type:markdown id: tags:
En cas d'imbrication de plusieurs boucles, l'instruction `break` sort de la plus imbriquée (la plus proche).
%% Cell type:code id: tags:
``` python
for i in (1, 2, 3):
for j in (1, 2, 3, 4):
if i == 2:
break
print(f"i, j = {i}, {j}")
```
%% Cell type:markdown id: tags:
### Instruction `continue`
Si, dans une boucle, on veut passer immédiatement à l'itération suivante, on utilise l'instruction `continue`.
%% Cell type:code id: tags:
``` python
compteur = 9
while compteur > 0:
compteur -= 1
if compteur % 2:
compteur /= 2
print('impair, on divise :', compteur)
continue # retourne immédiatement au début de la boucle
print("pair, RAS")
print("c'est fini...")
```
%% Cell type:markdown id: tags:
## Fonctions
Les fonctions permettent de réutiliser des blocs de code à plusieurs endroits différents sans avoir à copier ce bloc.
En python, il n'y a pas de notion de sous-routine. Les procédures sont gérées par les objets de type fonctions, avec ou sans valeur de retour.
```python
def <nom fonction>(arg1, arg2, ...):
<bloc d instructions>
return <valeur> # Instruction optionnelle
```
On distingue :
- les fonctions avec `return` des fonctions sans `return`
- les fonctions sans arguments (pour lesquelles `()` est vide) des fonctions avec arguments `(arg1, arg2, ...)`
`<nom fonction>(arg1, arg2, ...)` est appelé **signature** de la fonction.
%% Cell type:markdown id: tags:
### Fonctions sans arguments
#### Fonction sans ``return``
%% Cell type:markdown id: tags:
Pour définir une fonction :
%% Cell type:code id: tags:
``` python
def func(): # Definition de la fonction
print('You know what?')
print("I'm happy!")
```
%% Cell type:markdown id: tags:
Pour utiliser une fonction que l'on a défini :
%% Cell type:code id: tags:
``` python
func() # 1er Appel de la fonction
func() # 2eme appel
func() # 3eme appel, etc...
print(func()) # On l'appelle et on affiche sa valeur de retour
```
%% Cell type:markdown id: tags:
> **Exercice** : Ecrivez une fonction nommée `rien` qui ne fait rien et appelez là deux fois.
%% Cell type:code id: tags:
``` python
# Votre code ici
```
%% Cell type:code id: tags:
``` python
# Solution
def rien():
pass
rien()
rien()
```
%% Cell type:markdown id: tags:
#### Fonction avec ``return``
%% Cell type:code id: tags:
``` python
def func(): # Definition de la fonction
return "I'm happy" # La fonction retourne une chaine de caractère
print("1er appel:")
func() # 1er Appel de la fonction : la valeur retournée n'est pas utilisée
print("2eme appel:")
ret_val = func() # Le retour du 2eme appel est stocké
print("La fonction func() nous a renvoyé la valeur:", ret_val)
```
%% Cell type:markdown id: tags:
> **Exercice** : Ecrivez une fonction nommée `eloge_de_rien` qui retourne la chaine de caractères `rien`. Appelez-là et affichez sa valeur de retour.
%% Cell type:code id: tags:
``` python
# Votre code ici
```
%% Cell type:code id: tags:
``` python
# Solution
def eloge_de_rien():
"""Dédié à personne"""
return 'rien'
print(eloge_de_rien())
# Un premier aperçu de l'utilisation des docstrings :
help(eloge_de_rien)
```
%% Cell type:markdown id: tags:
**Important :** l'instruction `return` provoque une sortie de la fonction.
Dans le code suivant, la ligne qui appelle la fonction `print()` n'est pas exécutée.
%% Cell type:code id: tags:
``` python
def func():
return 'je sors'
print('après return')
func()
```
%% Cell type:markdown id: tags:
### Fonctions avec arguments
Pour définir une fonction qui prend des arguments, on nomme ces arguments entre les parenthèses de la ligne `def`. Ces paramètres seront définis comme des variables à l'intérieur de la fonction et recevrons les valeurs passées lors des appels de celle-ci.
%% Cell type:code id: tags:
``` python
def somme(x, y):
return x + y
```
%% Cell type:markdown id: tags:
Pour utiliser cette fonction avec diverses valeurs, il suffit de l'appeler plusieurs fois :
%% Cell type:code id: tags:
``` python
print(somme(1, 2))
print(somme(4, 7))
print(somme(2 + 2, 7))
print(somme(somme(2, 2), 7))
```
%% Cell type:markdown id: tags:
> **Exercice** :
- Définissez une fonction nommée `chevalier` qui prend un paramètre `n` et qui affiche `n` fois (avec `print()`) la chaîne de caractères `Nee!`
- appelez cette fonction pour vérifier que `chevalier(3)` dit bien `Nee!` trois fois comme il convient !
Voici quelques exemples montrant comment cette fonction doit se comporter:
```python
chevalier(1)
Nee!
chevalier(3)
Nee!Nee!Nee!
chevalier(6)
Nee!Nee!Nee!Nee!Nee!Nee!
```
%% Cell type:code id: tags:
``` python
def chevalier(n):
# Votre code ici
pass
```
%% Cell type:code id: tags:
``` python
# Vérifions que tout fonctionne bien:
chevalier(1)
chevalier(3)
chevalier(6)
```
%% Cell type:code id: tags:
``` python
# Solution
def chevalier(n):
print('Nee!' * n)
chevalier(3)
```
%% Cell type:markdown id: tags:
> **Exercice** : Ecrivez une autre fonction, nommée `chevalier_ret` :
>- qui prend 2 paramètres : un entier `n` et un booléen
>- qui retourne une chaine de caractères de la chaîne `nee!` ou `NEE!` en fonction du paramètre booléen, chaîne répétée `n` fois.
>
> Appelez cette fonction et affichez sa valeur de retour.
>
>Voici quelques exemples montrant comment cette fonction doit se comporter:
>
>```python
>a = chevalier_ret(1, True)
>print(a)
>NEE!
>a = chevalier_ret(3, False)
>print(a)
>nee!nee!nee!
>a = chevalier_ret(6, True)
>print(a)
>NEE!NEE!NEE!NEE!NEE!NEE!
>```
%% Cell type:code id: tags:
``` python
# Votre code ici
def chevalier_ret():
def chevalier_ret(n, cri):
pass
```
%% Cell type:code id: tags:
``` python
# Vérifions que tout fonctionne bien:
a = chevalier_ret(1, True)
print(a)
a = chevalier_ret(3, False)
print(a)
a = chevalier_ret(6, True)
print(a)
```
%% Cell type:code id: tags:
``` python
# Solution
def chevalier_ret(n, cri):
if cri:
retour = 'NEE!'
else:
retour = 'nee!'
return retour * n
print(chevalier_ret(3, True))
```
%% Cell type:markdown id: tags:
#### Utilisation de valeurs par défaut
%% Cell type:code id: tags:
``` python
def somme(x, y=1):
return x + y
print(somme(1, 2))
```
%% Cell type:markdown id: tags:
Si sa valeur n'est pas spécifiée lors de l'appel, le paramètre `y` prend la valeur par défaut (ici : `1`)
%% Cell type:code id: tags:
``` python
print(somme(4))
```
%% Cell type:markdown id: tags:
**Note :** Les arguments ayant une valeur par défaut doivent être placés **en dernier**.
%% Cell type:markdown id: tags:
#### Utilisation des arguments par leur nom
%% Cell type:markdown id: tags:
Si les arguments sont explicitiment nommés lors de l'appel, leur ordre peut être changé :
%% Cell type:code id: tags:
``` python
def diff(x, y):
return x - y
print(diff(4, 7))
print(diff(y=7, x=4))
```
%% Cell type:markdown id: tags:
#### Capture d'arguments non définis
##### Dans un tuple
On définit une fonction dont l'argument est `*args` :
%% Cell type:code id: tags:
``` python
def fonc(*args):
# args est un tuple :
print(type(args))
# ses éléments sont les arguments passés lors de l'appel :
print(args)
```
%% Cell type:markdown id: tags:
On l'appelle avec n'importe quelle séquence d'arguments :
%% Cell type:code id: tags:
``` python
fonc("n'importe", "quel nombre et type de", "paramètres", 5, [1, 'toto'], None)
```
%% Cell type:markdown id: tags:
##### Dans un dictionnaire
On définit une fonction dont l'argument est `**kwargs` :
%% Cell type:code id: tags:
``` python
def fonc(**kwargs):
# kwargs est un dictionnaire :
print(type(kwargs))
# ses éléments sont les arguments nommés passés lors de l'appel
print(kwargs)
```
%% Cell type:markdown id: tags:
On l'appelle en nommant les arguments :
%% Cell type:code id: tags:
``` python
fonc(x=1, y=2, couleur='rouge', epaisseur=2)
```
%% Cell type:markdown id: tags:
On peut combiner ce type d'arguments pour une même fonction :
%% Cell type:code id: tags:
``` python
def fonc(n, *args, **kwargs): # cet ordre est important
print("n =", n)
print("args =", args)
print("kwargs =", kwargs)
print("appel 1")
fonc(3)
print("appel 2")
fonc(3, 'bacon')
print("appel 3")
fonc(2, 'spam', 'egg', x=1, y=2, couleur='rouge', epaisseur=2)
```
%% Cell type:markdown id: tags:
**Remarques** :
- les noms `args` et `kwargs` ne sont que des conventions (à respecter, toutefois !), seul le caractère `*` est déterminant
- l'ordre `(arg1, arg2, ..., *args, **kwargs)` doit être strictement respecté
%% Cell type:markdown id: tags:
#### *Packing/unpacking*
- La syntaxe `*args` dans la définition de la fonction correspond à une opération de *packing* : Python transforme une séquence de variables en tuple.
- L'inverse existe : ça s'appelle l'*unpacking*.
- Le *packing/unpacking* se pratique déjà par la manipulation des tuples :
%% Cell type:code id: tags:
``` python
trio = "sax", "drums", "bass" # packing
print(trio)
you, her, him = trio # unpacking
print(you)
print(her)
print(him)
```
%% Cell type:markdown id: tags:
Il peut également se pratiquer dans le passage d'arguments de fonction
%% Cell type:code id: tags:
``` python
def fonc(*args):
print(args)
fonc(you, her, him) # ici on liste directement les arguments
fonc(*trio) # là on "unpack" un tuple
```
%% Cell type:markdown id: tags:
Et de la même façon pour un dictionnaire :
%% Cell type:code id: tags:
``` python
def fonc(**kwargs):
print(kwargs)
trio_dict = {"sax": "you", "drums": "her", "bass": "him"}
fonc(sax="you", drums="her", bass="him") # ici on liste directement les arguments nommés
fonc(**trio_dict) # là on "unpack" un dictionnaire
```
%% Cell type:markdown id: tags:
### Espace de nommage et portée des variables
#### 1er exemple
On veut illustrer le mécanisme de l'espace de nommage des variables :
%% Cell type:code id: tags:
``` python
def func1():
a = 1
print("Dans func1(), a =", a)
def func2():
print("Dans func2(), a =", a)
a = 2
func1()
func2()
print("Dans l'espace englobant, a =", a)
```
%% Cell type:markdown id: tags:
Cet exemple montre deux comportements :
1. Une variable définie localement à l'intérieur d'une fonction cache une variable du même nom définie dans l'espace englobant (cas de `func1()`).
2. Quand une variable n'est pas définie localement à l'intérieur d'une fonction, Python va chercher sa valeur dans l'espace englobant (cas de `func2()`).
%% Cell type:markdown id: tags:
#### 2ème exemple
On veut illustrer le mécanisme de portée des variables au sein des fonctions :
%% Cell type:code id: tags:
``` python
def func():
a = 1
bbb = 2
print('Dans func() : a =', a)
a = 2
print("Avant func() : a =", a)
func()
print("Après func() : a =", a)
```
%% Cell type:markdown id: tags:
Les variables définies localement à l'intérieur d'une fonction sont détruites à la sortie de cette fonction. Ici, la variable ``bbb`` n'existe pas hors de la fonction ``func()``, donc Python renvoie une erreur si on essaye d'utiliser ``bbb`` depuis l'espace englobant :
%% Cell type:code id: tags:
``` python
# Cette cellule génère une erreur
print(bbb)
```
%% Cell type:markdown id: tags:
### Fonctions *built-in*
Ces fonctions sont disponibles dans tous les contextes. La liste complète est détaillée [ici](https://docs.python.org/3/library/functions.html#). En voici une sélection :
- `dir(obj)` : retourne une liste de toutes les méthodes et attributs de l'objet `obj`
- `dir()` : retourne une liste de tous les objets du contexte courant
- `eval(expr)` : analyse et exécute la chaîne de caractère `expr`
%% Cell type:code id: tags:
``` python
a = 1
b = eval('a + 1')
print("b est de type", type(b), "et vaut", b)
```
%% Cell type:markdown id: tags:
- `globals()` : retourne un dictionnaire des variables présentes dans le contexte global
- `locals()` : idem `globals()` mais avec le contexte local
- `help(obj)` : affiche l’aide au sujet d’un objet
- `help()` : affiche l’aide générale (s'appelle depuis l'interpréteur interactif)
%% Cell type:markdown id: tags:
- `input(prompt)` : retourne une chaîne de caractère lue dans la console après le message `prompt`
%% Cell type:code id: tags:
``` python
reponse = input('Ca va ? ') # Seule la variante input() fonctionne dans un notebook
if reponse.lower() in ('o', 'oui', 'yes', 'y', 'ok', 'da', 'jawohl', 'ja'):
print('Supercalifragilisticexpialidocious')
else:
print('Faut prendre des vacances...')
```
%% Cell type:markdown id: tags:
- `len(seq)` : retourne la longueur de la séquence `seq`
- `max(seq)` : retourne le maximum de la séquence `seq`
- `min(seq)` : retourne le minimum de la séquence `seq`
- `range([start=0], stop[, step=1])` : retourne un itérateur d'entiers allant de `start` à `stop - 1`, par pas de `step`.
%% Cell type:code id: tags:
``` python
print(list(range(10)))
print(list(range(5, 10, 2)))
```
%% Cell type:markdown id: tags:
- `repr(obj)`: affiche la représentation de l'objet `obj`.
- `reversed(seq)` : retourne l’inverse de la séquence `seq`
- `sorted(seq)` : retourne une séquence triée à partir de la séquence `seq`
- `sum(seq)` : retourne la somme des éléments de la séquence `seq`
%% Cell type:markdown id: tags:
### Exercices sur les fonctions
%% Cell type:markdown id: tags:
#### Exercice 1
Ecrire une fonction ``stat()`` qui prend en argument une liste d'entiers et retourne un tuple contenant :
- la somme
- le minimum
- le maximum
des éléments de la liste
%% Cell type:code id: tags:
``` python
def stat(a_list):
# votre fonction
pass
stat([1, 4, 6, 9])
```
%% Cell type:code id: tags:
``` python
# Solution
def stat(a_list):
"""return sum, min and max of a list as a tuple"""
return sum(a_list), min(a_list), max(a_list)
print(stat([1, 4, 6, 9]))
def stat(*args):
"""return sum, min and max of a sequence of arguments as a tuple"""
return sum(args), min(args), max(args)
print(stat(1, 4, 6, 9))
```
%% Cell type:markdown id: tags:
<a id='wrapper'></a>
#### Exercice 2 : écriture d'un *wrapper* de fonction
Noël arrive vite... Amusez-vous avec les boules de décoration !
![sapin](fig/sapin.jpg)
Soit une fonction ``boule()`` capable d'accrocher une boule de couleur à la position ``(x, y)`` d'un sapin.
> <span style="font-size:0.7em;">Exercice inspiré du Mooc de l'INRIA [Python : des fondamentaux à l'utilisation du langage](https://www.fun-mooc.fr/courses/course-v1:UCA+107001+session02/about)</span>
%% Cell type:code id: tags:
``` python
def boule(x, y, couleur='bleue'):
print(f"J'accroche une boule en ({x}, {y}) de couleur {couleur}")
# On place la première boule sur le sapin
boule(1, 2)
# Puis une autre, etc.
boule(3, 4, couleur='rouge')
```
%% Cell type:markdown id: tags:
Ecrire une fonction *wrapper* `boule_or()` qui crée des boules dorées en appelant la fonction `boule()`. Dans le futur, on souhaite modifier la fonction `boule()` pour lui faire accepter un nouvel argument `rendu (brillant, mat, etc.)`.
La fonction `boule_or()` devra continuer à fonctionner après cette modification de la fonction `boule()` et intégrer la nouvelle fonctionnalité `rendu` sans qu'il soit nécessaire de la modifier.
%% Cell type:code id: tags:
``` python
# Cellule à modifier
def boule_or(*args, **kwargs):
# Votre code ici
pass
# On place une boule en or sur le sapin
boule_or(1, 2)
```
%% Cell type:markdown id: tags:
Maintenant, on met à jour la fonction ``boule()`` :
%% Cell type:code id: tags:
``` python
def boule(x, y, couleur='bleue', rendu='mat'):
print(f"J'accroche une boule en ({x}, {y}) de couleur {couleur} et de rendu {rendu}.")
boule(1, 3, couleur='jaune', rendu='brillant')
```
%% Cell type:markdown id: tags:
Vérifier que votre fonction ``boule_or()`` marche encore et gère la nouvelle fonctionnalité :
%% Cell type:code id: tags:
``` python
boule_or(3, 1, rendu='brillant') # doit retourner :
# J'accroche une boule en (3, 1) de couleur or et de rendu brillant.
```
%% Cell type:code id: tags:
``` python
# Solution
def boule_or(*args, **kwargs):
# seule instruction qui fait une hypothèse
# sur la signature de la fonction boule() :
kwargs['couleur'] = "or"
return boule(*args, **kwargs)
boule_or(1, 2, rendu='brillant')
```
%% Cell type:markdown id: tags:
## Exceptions
Pour signaler des conditions particulières (erreurs, évenements exceptionnels), Python utilise un mécanisme de levée d'exceptions.
%% Cell type:code id: tags:
``` python
# Cette cellule génère une erreur
raise Exception
```
%% Cell type:markdown id: tags:
Ces exceptions peuvent embarquer des données permettant d'identifier l'évenement producteur.
%% Cell type:code id: tags:
``` python
# Cette cellule génère une erreur
raise Exception('Y a une erreur')
```
%% Cell type:markdown id: tags:
La levée d'une exception interrompt le cours normal de l'exécution du code et "remonte" jusqu'à l'endroit le plus proche gérant cette exception.
Pour intercepter les exceptions, on écrit :
```python
try:
<bloc de code 1>
except Exception:
<bloc de code 2>
```
%% Cell type:code id: tags:
``` python
try:
print('ici ca fonctionne')
# ici on détecte une condition exceptionnelle, on signale une exception
raise Exception('y a un bug')
print("on n'arrive jamais ici")
except Exception as e:
# L'excécution continue ici
print(f"ici on peut essayer de corriger le problème lié à l'exception : Exception({e})")
print("et après, ça continue ici")
```
%% Cell type:markdown id: tags:
Exemple illustrant le mécanisme de remontée des exceptions d'un bloc à l'autre :
%% Cell type:code id: tags:
``` python
def a():
raise Exception('coucou de a()')
def b():
print('b() commence')
a()
print('b() finit')
try:
b()
except Exception as e:
print("l'exception vous envoie le message :", e)
```
%% Cell type:markdown id: tags:
### Exercice
Ecrivez une fonction `openfile()` :
- qui demande à l'utilisateur un nom de fichier à ouvrir
- qui gère correctement les fichiers inexistants.
- qui affiche la première ligne du fichier ouvert
- qui retourne une valeur booléenne indiquant que le fichier a été ouvert ou non.
> **Attention :** Python attend un nom de fichier complet. Sous Windows, les extensions de fichier sont cachées par défaut...
%% Cell type:code id: tags:
``` python
# Votre code ici
def openfile():
pass
print(openfile())
```
%% Cell type:code id: tags:
``` python
# Solution
def openfile():
filename = input('Donnez un chemin de fichier : ').strip()
try:
f = open(filename, 'r')
print(f.readline())
f.close()
return True
except FileNotFoundError as err:
# Ignore les erreurs lorsqu'on essaie d'ouvrir le fichier
pass
return False
print(openfile())
```
%% Cell type:markdown id: tags:
Pour plus d'informations sur les exceptions, lire ce [tutoriel](https://docs.python.org/3/tutorial/errors.html).
%% Cell type:markdown id: tags:
## Les gestionnaires de contexte
Pour faciliter la gestion des obligations liées à la libération de ressources, la fermeture de fichiers, etc... Python propose des gestionnaires de contexte introduits par le mot-clé `with`.
%% Cell type:code id: tags:
``` python
with open('exos/interessant.txt', 'r') as fichier_ouvert:
# Dans ce bloc de code le fichier est ouvert en lecture, on peut l'utiliser normalement
print(fichier_ouvert.read())
# Ici, on est sorti du bloc et du contexte : le fichier à été fermé automatiquement
```
%% Cell type:code id: tags:
``` python
# Cette cellule génère une erreur
print(fichier_ouvert.read())
```
%% Cell type:markdown id: tags:
> **Exercice** : Reprenez le code de l'exercice précédent, et utilisez ``with`` pour ne pas avoir à utiliser la méthode ``close()``.
%% Cell type:code id: tags:
``` python
# Votre code ici
```
%% Cell type:code id: tags:
``` python
# Solution
def openfile():
filename = input('Donnez un chemin de fichier : ')
try:
with open(filename, 'r') as f:
print(f.readline())
return True
except FileNotFoundError as err:
# Ignore errors while trying to open file
pass
return False
print(openfile())
```
%% Cell type:markdown id: tags:
Il est possible de créer de nouveaux gestionnaires de contexte, pour que vos objets puissent être utilisés avec ``with`` et que les ressources associées soient correctement libérées.
Pour plus d'informations sur la création de gestionnaires de contexte, voir [la documentation](https://docs.python.org/3/library/stdtypes.html#context-manager-types).
%% Cell type:markdown id: tags:
## Les compréhensions de listes
Python a introduit une facilité d'écriture pour les listes qui permet de rendre le code plus lisible car plus concis.
On construit par exemple la liste `[0, 1, 2, ..., 9]` :
%% Cell type:code id: tags:
``` python
Liste1 = list(range(10))
print(Liste1)
```
%% Cell type:markdown id: tags:
On veut maintenant une liste ne contenant que les éléments pairs de la liste Liste1.
%% Cell type:code id: tags:
``` python
ListePaire = []
for i in Liste1:
if (i % 2) == 0:
ListePaire.append(i)
print(ListePaire)
```
%% Cell type:markdown id: tags:
À présent, on fait la même chose en compréhension de liste
%% Cell type:code id: tags:
``` python
ListePaire = [i for i in Liste1 if (i % 2) == 0]
print(ListePaire)
```
%% Cell type:markdown id: tags:
Cette concision peut être utile, mais n'en abusez pas, si vous commencez à avoir une compréhension de liste trop complexe à écrire en une simple ligne, utilisez plutôt des boucles et conditions explicites.
Plus d'informations dans [ce tutoriel](https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions).
%% Cell type:markdown id: tags:
## Les expressions génératrices
C'est une forme d'écriture, très proche des compréhensions de listes, mais qui ne crée pas de nouvel objet liste immédiatement. Les items sont produits à la demande, plus loin dans le code, là où ils sont nécessaires, ce qui économise du temps et de la mémoire.
%% Cell type:code id: tags:
``` python
generateur_pairs = (i for i in Liste1 if (i % 2) == 0)
```
%% Cell type:markdown id: tags:
Le générateur ne contient pas de données à proprement parler :
%% Cell type:code id: tags:
``` python
print(generateur_pairs)
```
%% Cell type:markdown id: tags:
Pour visualiser son comportement, on peut l'utiliser pour créer une liste :
%% Cell type:code id: tags:
``` python
print(list(generateur_pairs))
```
%% Cell type:markdown id: tags:
Plus d'informations dans [ce tutoriel](https://docs.python.org/3/tutorial/classes.html#generator-expressions).
%% Cell type:markdown id: tags:
## Modules
- Python fournit un système de modularisation du code qui permet d'organiser un projet contenant de grandes quantités de code et de réutiliser et de partager ce code entre plusieurs applications.
- L'instruction `import` permet d'accéder à du code situé dans d'autres fichiers. Cela inclut les nombreux modules de la bibliothèque standard, tout comme vos propres fichiers contenant du code.
- Les objets (variables, fonctions, classes, etc.) du module sont accessibles de la manière suivante :
```python
module.variable
module.fonction(parametre1, parametre2, ...)
```
Ou plus généralement :
```python
module.attribut
```
%% Cell type:code id: tags:
``` python
# Pour utiliser les fonctions mathématiques du module 'math'
import math
print(f'{math.pi:.2f}')
print(f'{math.sin(math.pi):.2f}')
```
%% Cell type:markdown id: tags:
### Créer ses propres modules
- Il suffit de placer votre code dans un fichier avec l'extension `.py`
- Le module stocké dans le fichier `mon_module.py` s'importe avec la syntaxe :
```python
import mon_module
```
%% Cell type:markdown id: tags:
#### Cas 1 : le fichier module est directement importable
- **Cas 1.a :** le module est déjà installé dans l'environnement d'exécution
- soit il fait partie de la bibliothèque standard
- soit il a été installé (avec `conda`, avec `pip`, etc.)
- **Cas 1.b :** le fichier module se trouve :
- dans le même répertoire que le script qui l'importe
- dans un répertoire référencé par la variable d'environnement `PYTHONPATH`
Le fichier `exos/mon_module.py` contient du code définissant `ma_variable` et `ma_fonction()`.
On copie ce fichier dans le répertoire courant.
%% Cell type:code id: tags:
``` python
import shutil # shutil fait partie de la bibliothèque standard
shutil.copy("exos/mon_module.py", ".") # On copie le fichier dans le répertoire d'exécution du notebook
%pycat mon_module.py
```
%% Cell type:markdown id: tags:
On peut maintenant importer `mon_module` depuis le notebook :
%% Cell type:code id: tags:
``` python
import mon_module # Notez la syntaxe: nom_du_fichier sans l'extension.
print(mon_module.ma_variable)
mon_module.ma_fonction() # On accède ainsi à l'attribut ma_fonction() du module mon_module
```
%% Cell type:markdown id: tags:
On peut importer un module sous un autre nom (pour le raccourcir, en général) :
%% Cell type:code id: tags:
``` python
import mon_module as mm
mm.ma_fonction()
```
%% Cell type:markdown id: tags:
On peut importer un objet particulier d'un module :
%% Cell type:code id: tags:
``` python
from mon_module import ma_fonction
ma_fonction()
```
%% Cell type:markdown id: tags:
Ou encore en définissant un alias avec `as`
%% Cell type:code id: tags:
``` python
from mon_module import ma_fonction as ma_fonc
ma_fonc()
```
%% Cell type:markdown id: tags:
**Note** :
Dans le code :
```python
import mon_module
[...]
import mon_module
```
Le deuxième `import` n'a pas d'effet car le module n'est importé qu'une seule fois au sein d'une même instance Python. Toutefois, il existe des moyens de le réimporter de force avec la bibliothèque [importlib](https://docs.python.org/3/library/importlib.html#importlib.reload).
%% Cell type:markdown id: tags:
> **Exercice** : Modifiez le code contenu dans le fichier [mon_module.py](mon_module.py), et reexécutez la cellule ci-dessus. Que remarquez-vous ?
%% Cell type:markdown id: tags:
#### Cas 2 : le fichier module se trouve ailleurs
On peut distinguer deux cas d'usage :
- **Cas 2.a :** on veut aller chercher du code dans un autre projet python qui n'est pas installé dans l'environnement courant
- **Cas 2.b :** on travaille sur un gros projet structuré en modules stockés dans une arborescence de sous-répertoires.
%% Cell type:markdown id: tags:
##### Cas 2.a : exemple avec le fichier `exos/ext_dir/module_externe.py`
Le fichier `module_externe.py` contient :
%% Cell type:code id: tags:
``` python
%pycat exos/ext_dir/module_externe.py
```
%% Cell type:markdown id: tags:
On ajoute le répertoire `exos/ext_dir` à la liste des répertoires scannés par Python :
%% Cell type:code id: tags:
``` python
import sys
#from pathlib import Path # Pour la compatibilité avec Windows
# il est nécessaire de convertir l'objet pathlib.Path en chaîne de caractères
#sys.path.append(str(Path("./exos/ext_dir")))
sys.path.append("./exos/ext_dir")
```
%% Cell type:markdown id: tags:
Le module s'importe alors directement avec :
%% Cell type:code id: tags:
``` python
import module_externe
```
%% Cell type:markdown id: tags:
##### Cas 2.b : un projet structuré en sous-répertoires
Dans ce cas, on parle de paquets (*packages*) et de sous-paquets :
- n'importe quel répertoire est un **paquet python**
- chaque sous-répertoire est un sous-paquet du répertoire (ou paquet) parent
- une arborescence de paquets permet d'organiser le code de manière hiérarchique.
- On accède aux sous-paquets avec la notation :
```python
import paquet.sous_paquet.sous_sous_paquet...
```
%% Cell type:markdown id: tags:
###### Exemple avec le répertoire `exos` :
%% Cell type:code id: tags:
``` python
#!tree exos -P "*.py" -I __pycache__
```
%% Cell type:markdown id: tags:
```bash
exos
[...]
├── sous_paquet
│ ├── __init__.py
│ ├── module_a.py
│ └── module_b.py
├── sous_paquet2
│ └── module_c.py
[...]
```
%% Cell type:markdown id: tags:
Dans ce cas, le module `exos/sous_paquet/module_a.py` contenant :
%% Cell type:code id: tags:
``` python
%pycat exos/sous_paquet/module_a.py
```
%% Cell type:markdown id: tags:
s'importe de la façon suivante :
%% Cell type:code id: tags:
``` python
import exos.sous_paquet.module_a
# On appelle fonction()
exos.sous_paquet.module_a.fonction()
```
%% Cell type:markdown id: tags:
ou encore :
%% Cell type:code id: tags:
``` python
from exos.sous_paquet import module_a
# On appelle fonction()
module_a.fonction()
```
%% Cell type:markdown id: tags:
> **Exercice :** Importer directement la fonction `fonction` de `exos/sous_paquet/module_b.py` sous le nom `func` et appelez-là.
%% Cell type:code id: tags:
``` python
%pycat exos/sous_paquet/module_b.py
```
%% Cell type:code id: tags:
``` python
# Votre code ici
from exos.sous_paquet.module_b import fonction as func
func()
# func()
```
%% Cell type:code id: tags:
``` python
# Solution
from exos.sous_paquet.module_b import fonction as func
func()
```
%% Cell type:markdown id: tags:
#### Remarque
En python2, `__init__.py` était obligatoire pour que le répertoire qui le contient soit considéré comme un paquet.
En python3, `__init__.py` est optionnel : tout répertoire peut être un paquet.
Si `__init__.py` existe, le code qu'il contient est exécuté lors de l'import du paquet.
%% Cell type:code id: tags:
``` python
%pycat exos/sous_paquet/__init__.py
```
%% Cell type:markdown id: tags:
Dans ce cas, on peut importer directement `module_a` et `module_b` :
%% Cell type:code id: tags:
``` python
from exos.sous_paquet import *
module_b.fonction()
```
%% Cell type:markdown id: tags:
### Imports relatifs
Depuis le module
```bash
exos/sous_paquet2/module_c.py
```
on peut importer le module
```bash
exos/sous_paquet/module_b.py
```
en utilisant la syntaxe :
```python
from .. import sous_paquet
from ..sous_paquet import module_b
```
%% Cell type:code id: tags:
``` python
%pycat exos/sous_paquet2/module_c.py
```
%% Cell type:markdown id: tags:
On importe `module_c` et on appelle ses attributs :
%% Cell type:code id: tags:
``` python
from exos.sous_paquet2 import module_c
module_c.fonction()
module_c.module_b.fonction()
```
%% Cell type:markdown id: tags:
Pour plus d'informations sur les modules et paquets, voir [ce tutoriel](https://docs.python.org/3/tutorial/modules.html).
%% Cell type:markdown id: tags:
### Exercice
> 1. dans le répertoire [exos/sous_paquet2/](../tree/exos/sous_paquet2) copiez la définition de la fonction `boule()` (cf. [exercice ci-dessus](#wrapper)) dans un fichier nommé `deco.py`
> 2. dans le répertoire [exos/sous_paquet/](../tree/exos/sous_paquet) éditez un fichier nommé `noel.py` contenant :
> - l'import de la fonction `boule()` depuis `exos/sous_paquet2/deco.py`
> - la définition de la fonction `boule_or()` (cf. corrigé de l'exercice)
>
> ```python
> def boule_or(*args, **kwargs):
> # seule instruction qui fait une hypothèse
> # sur la signature de la fonction boule() :
> kwargs['couleur'] = "or"
> return boule(*args, **kwargs)
> ```
>
> 3. Décommentez la cellule ci-dessous, et exécutez-là pour vérifier que la fonction `boule_or()` est bien importée.
%% Cell type:code id: tags:
``` python
#from exos.sous_paquet.noel import boule_or
#boule_or(1, 2)
```
%% Cell type:code id: tags:
``` python
%%writefile exos/sous_paquet2/deco.py
def boule(x, y, couleur='bleue', rendu='mat'):
print("J'accroche une boule en ({}, {}) de couleur {} et de rendu {}.".format(
x, y, couleur, rendu))
```
%% Cell type:code id: tags:
``` python
%%writefile exos/sous_paquet/noel.py
from ..sous_paquet2.deco import boule
def boule_or(*args, **kwargs):
# seule instruction qui fait une hypothèse
# sur la signature de la fonction boule() :
kwargs['couleur'] = "or"
return boule(*args, **kwargs)
```
%% Cell type:code id: tags:
``` python
from exos.sous_paquet.noel import boule_or
boule_or(1, 2)
```
%% Cell type:markdown id: tags:
### Quelques modules de la stdlib
La bibliothèque standard de Python est incluse dans toute distribution de Python. Elle contient en particulier une panoplie de modules à la disposition du développeur.
#### ``string``
- ``find()``
- ``count()``
- ``split()``
- ``join()``
- ``strip()``
- ``upper()``
- ``replace()``
%% Cell type:markdown id: tags:
#### ``math``
- ``log()``
- ``sqrt()``
- ``cos()``
- ``pi``
- ``e``
%% Cell type:markdown id: tags:
#### ``os``
- ``listdir()``
- ``getcwd()``
- ``getenv()``
- ``chdir()``
- ``environ()``
- ``os.path : exists(), getsize(), isdir(), join()``
%% Cell type:markdown id: tags:
#### ``sys``
- ``argv``
- ``exit()``
- ``path``
%% Cell type:markdown id: tags:
Mais bien plus sur la [doc officielle de la stdlib](https://docs.python.org/3/library/) !
%% Cell type:markdown id: tags:
## Bonnes pratiques
### Commentez votre code
- pour le rendre plus lisible
- pour préciser l'utilité des fonctions, méthodes, classes, modules, etc...
- pour expliquer les parties complexes
%% Cell type:markdown id: tags:
### Documentez avec Docstring
- Juste après la signature de la fonction, on utilise une chaîne de caractère appelée *docstring* délimitée par `""" """`
- la *docstring* permet de documenter la fonction au plus près de sa définition
- cette *docstring* est affichée par `help(fonction)`
- la *docstring* est utilisée par les outils de documentation automatique comme [Sphinx](http://www.sphinx-doc.org).
%% Cell type:code id: tags:
``` python
def nrien(n):
"""Retourne n fois 'rien'"""
return 'rien' * n
help(nrien)
# ou dans ipython:
nrien?
```
%% Cell type:markdown id: tags:
Plus d'information sur les Docstrings dans la [documentation officielle](https://www.python.org/dev/peps/pep-0257/) et l'extension [Napoleon](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/index.html) pour [Sphinx](http://www.sphinx-doc.org).
%% Cell type:markdown id: tags:
### Conventions d'écriture
Habituez-vous assez tôt aux conventions préconisées dans la communauté des utilisateurs de python.
Cela vous aidera a relire plus facilement le code écrit par d'autres, et aidera les autres (et vous-même !) à relire votre propre code.
Ces conventions sont décrites dans le document [PEP n°8](https://www.python.org/dev/peps/pep-0008/) (Python Enhancement Proposal).
L'outil [pep8](https://pypi.python.org/pypi/pep8) permet d'automatiser la vérification du respect de ces règles.
%% Cell type:markdown id: tags:
> **Exercice** :
>
> 1. Lisez le PEP8, et corrigez toutes les fautes commises dans ce notebook
> 2. Envoyez le résultat à votre enseignant
%% Cell type:markdown id: tags:
### Organisez votre code source
Pour la lisibilité, la réutilisation et la maintenance, le principe à retenir est **d'évitez le copier-coller** de code :
- placez dans une même fonction les portions de code exécutées plusieurs fois
- placez dans un même module les variables, fonctions et classes partagées entre plusieurs parties ou programmes
- dans les projets importants, regrouper vos modules en packages
%% Cell type:markdown id: tags:
## Utiliser les environnements de développement intégrés :
* [pydev](http://pydev.org)
* [eclipse](http://www.eclipse.org)
* [spyder](https://www.spyder-ide.org)
* [pycharm](https://www.jetbrains.com/pycharm)
![](fig/spyder.png)
%% Cell type:markdown id: tags:
### Utilisez un gestionnaires de versions
* [git](http://www.git-scm.com) : gestion distribuée, largement majoritaire aujourd'hui
* [subversion](https://subversion.apache.org) : gestion centralisée, outil plus ancien
Faire des enregistrements *(commits)* fréquents et cohérents.
### Héberger vos dépôts de sources
* [github](https://github.com) : des millions d'utilisateurs et de dépôts
* [gitlab](https://gitlab.com) : un concurrent moins visibles mais aux fonctionnalités très intéressantes
%% Cell type:markdown id: tags:
### Visualisation de différences :
* [tkdiff](http://tkdiff.sourceforge.net)
* [kdiff3](http://kdiff3.sourceforge.net)
* [meld](http://meldmerge.org) - écrit en python !
![](fig/meld.png)
%% Cell type:markdown id: tags:
### Vérificateurs de code source
* [pep8](https://pypi.python.org/pypi/pep8) : pour normaliser la mise en page
* [pylint](http://www.pylint.org) : pour vérifier que la syntaxe est correcte
Ces modules sont embarqués dans les IDE avancées.
%% Cell type:markdown id: tags:
### Tests en Python
En programmation, on utilise des tests de non régression pour vérifier que les nouveaux développements et les corrections de bugs n'entraînent pas de pertes de fonctionalités et de nouveaux bugs.
On distingue généralement :
- les tests unitaires (comportement de fonctions prises séparément)
- les tests d'intégration (interaction entre plusieurs parties de programme)
- les tests du système complet
Python dispose de nombreux modules dédiés aux deux premières catégories. Quelques exemples :
* [`unittest`](https://docs.python.org/3/library/unittest.html) : fait partie de la bibliothèque standard
* [`doctest`](https://docs.python.org/3/library/doctest.html) : le test est basé sur des sorties de sessions interactives stockées généralement dans la *docstring* de la fonction testée (alourdit le code...)
* [`nose`](https://nose.readthedocs.io) : une extension de `unittest`
* [`pytest`](http://pytest.org) : syntaxe simple et nombreuses fonctionnalités
Une synthèse des outils existants sur [cette page](https://wiki.python.org/moin/PythonTestingToolsTaxonomy).
%% Cell type:markdown id: tags:
### Environnements virtuels
Les environnements virtuels sont très utiles pour le développement car ils permettent d'isoler les installations et les exécutions.
* [virtualenv](https://virtualenv.pypa.io)
* [conda environments](https://conda.io/docs/user-guide/tasks/manage-environments.html)
%% Cell type:markdown id: tags:
## Philosophie du langage : le zen de Python
![](fig/python-logo.png)
[PEP 20](https://www.python.org/dev/peps/pep-0020)
%% Cell type:code id: tags:
``` python
import this
```
%% Cell type:markdown id: tags:
## Python 3.x vs 2.x
C'est le futur, et incidemment aussi le présent voire même le passé...
- Quoi : une version qui casse la compatibilité ascendante
- Pourquoi : nettoyage de parties bancales du langage accumulées au fil du temps
- Python 3.0 est sorti en 2008
- Python 2.7 est sorti en 2010 : EOL, fin de vie, (mal-)heureusement longue à venir
- Un certain nombre de choses n'a pas encore été converti pour fonctionner avec
- Les distributions linux majeures proposent encore la 2.X par défaut, mais la 3 est disponible en parallèle
- Une partie, la moins disruptive, a quand même été portée vers 2.6 et 2.7 pour aider à la transition
- Les tutoriels, et autres documentations disponibles sur internet ne sont pas forcément migrées
- Pour un nouveau projet, recherchez un peu avant de vous lancer, pour vérifier les besoins en librairies externes
- Les implémentations tierces d'interpréteurs python peuvent avoir des degrés variables de compatibilité avec les versions 3.x
- Les modules comportant des extentions en C sont plus compliqués à porter.
%% Cell type:markdown id: tags:
### Différences notables entre Python 2 et Python 3
- Division entière
- ``print()``
- variable à durée de vie plus stricte (boucles, etc...)
- toutes les classes sont du nouveau type
- Les chaînes de caractères sont en UTF-8 par défaut & encoding(s) & byte() interface
- stdlib changée
- ``range()`` vs ``xrange()``
- outils ``2to3.py, 3to2, python-modernize, futurize``
- ``pylint --py3k``
- module de compatibilité : `six`
Plus d'informations sur le [wiki officiel](https://wiki.python.org/moin/Python2orPython3).
%% Cell type:markdown id: tags:
## Exercice de synthèse : décodage de l'ARN
### Enoncé
On souhaite traduire une séquence d'ARN stockée dans le fichier [exos/arn.txt](exos/arn.txt) en une séquence d'acides aminés.
Pour ce faire, on dispose du code génétique (simplifié) stocké dans [exos/code_genetique.txt](exos/code_genetique.txt) (tableau tiré de [wikipedia](https://fr.wikipedia.org/wiki/Code_g%C3%A9n%C3%A9tique#Table_des_codons_d'ARN_messager)).
Le fichier `code_genetique.txt` contient pour chaque ligne :
```
Nom de l'acide aminé; lettre unique; nom abrégé; codon1, codon2, etc.
```
%% Cell type:code id: tags:
``` python
%pycat exos/code_genetique.txt
```
%% Cell type:markdown id: tags:
### Consignes
1. Ecrire le code qui :
- ouvre le fichier code génétique
- lit son contenu pour générer le dictionnaire suivant :
```python
code = {letter: {name: ...,
abrv: ...,
codons: [..., ..., ]}}
```
%% Cell type:code id: tags:
``` python
# votre code ici
```
%% Cell type:code id: tags:
``` python
# 1. on construit le dictionnaire code = {letter: {name: , abrv:, etc.}}
code = {}
with open("exos/code_genetique.txt", 'r') as f:
for line in f:
# Chaque ligne est lue pour être stockée dans un dictionnaire
parts = line.split(";")
name = parts[0]