<jupyter_start><jupyter_text>Un avant-goût de tout  RappelsUn programme informatique manipule des variables pour obtenir un résultat. Une variable dispose de caractéristiques, comme par exemple :- la valeur qui lui est affectée ;- le type de la valeur affectée.Selon son type, une variable dispose de fonctions, appelées méthodes, que l’on peut lister en utilisant la fonction `help()`. Comment, par exemple, transformer le texte suivant en majuscules ?<jupyter_code># The variable "text" is an instance of the String class.
text = "A Lannister always pays his debts."
# 'print' is a function, while 'upper' is a method of the String class.
print(text.upper())<jupyter_output><empty_output><jupyter_text>Un problème de réductionUn programme informatique sert à résoudre un problème. Soit la situation suivante :> C’est les soldes ! Des vêtements, des étiquettes avec un prix initial, une réduction en pourcentage et un prix réduit. Mais voilà, vous vous êtes toujours demandé si le prix réduit correspondait bien à chaque fois à la réduction annoncée. Parfois, c’est facile à calculer de tête ; d’autres fois, beaucoup moins. Et si vous tentiez de régler la question avec un programme informatique ? Pour construire un programme informatique, l’impondérable c’est la phase de conception. En résumé, de quoi a-t-on besoin pour parvenir au résultat ?Pour le problème de la solde, on a besoin de connaître le prix de départ ainsi que le taux de réduction :<jupyter_code>price = 17
discount = 30<jupyter_output><empty_output><jupyter_text>Il reste à appliquer la formule mathématique :1. un taux de 30 %, c’est 30/100 ;2. calculer la réduction, c’est multiplier le prix initial par le taux ;3. calculer le prix réduit, c’est soustraire la réduction au prix initial.<jupyter_code>formula = price - (price * (discount / 100))<jupyter_output><empty_output><jupyter_text>Puis à l’afficher :<jupyter_code>print(formula)<jupyter_output><empty_output><jupyter_text>Interagir avec l’utilisateurOk, mais notre programme ne fonctionne que pour un cas unique. Il est encore moins performant qu’une calculette de poche…Pour l’améliorer, on va permettre à un utilisateur de saisir des données plutôt que de les coder en dur :<jupyter_code>price = input("Quel est le prix d’origine ?")
discount = input("Quelle est la réduction ?")<jupyter_output><empty_output><jupyter_text>Malheureusement, une erreur survient lorsque l’on exécute la formule. Python nous prévient qu’il s’agit d’une erreur de type :<jupyter_code>formula = price - (price * (discount / 100))<jupyter_output><empty_output><jupyter_text>Une saisie utilisateur est toujours de type `str`. Pour la rendre compatible avec des opérations mathématiques, il faut la transformer en type `int` : Python n’exécute aucune conversion implicite !<jupyter_code>price = int(price)
discount = int(discount)
formula = price - (price * (discount / 100))
print(formula)<jupyter_output><empty_output><jupyter_text>Plutôt que de ne renvoyer qu’un nombre, ce serait encore mieux d’écrire un message explicite à l’utilisateur :<jupyter_code>print(f"Le prix soldé est de {formula} €.")<jupyter_output><empty_output><jupyter_text>Cerise sur le gâteau, les conventions syntaxiques en français définissant la virgule comme séparateur des décimales, on utilise la méthode `replace()` de la classe `str` pour remplacer le point :<jupyter_code>result = str(formula)
print(f"Le prix soldé est de {result.replace('.', ',')} €.")<jupyter_output><empty_output><jupyter_text>Définir une fonctionLa formule de calcul d’une réduction est assez simple à utiliser comme cela. Le seul inconvénient est de toujours veiller à ce qu’elle soit placée **après** l’affectation des variables `price` et `discount`. En puis, évidemment, il ne faut pas modifier l’ordre des opérations… Pour garantir l’intégrité d’une opération, une pratique courante consiste à définir une fonction en tête du programme :<jupyter_code>def sales():
    pass<jupyter_output><empty_output><jupyter_text>Le mot-clé `def` permet de définir une fonction. Une fonction dispose d’un nom à la discrétion du développeur. Ce nom permettra ensuite de l’appeler pour obtenir le résultat escompté :<jupyter_code>def sales():
    price = input("Quel est le prix d’origine ?")
    discount = input("Quelle est la réduction ?")
    price = int(price)
    discount = int(discount)
    formula = price - (price * (discount / 100))
    result = str(formula)
    return f"Le prix soldé est de {result.replace('.', ',')} €."
sales()<jupyter_output><empty_output><jupyter_text>La fonction `sales()` est bien trop spécifique pour être vraiment utile, et pour la rendre générique, il faut gagner en abstraction. Une fonction accepte des paramètres en entrée, un peu comme une recette exige des ingrédients pour parvenir au résultat. Ces paramètres constituent la signature de la fonction :<jupyter_code>def sales(price, discount):
    result = price - (price * (discount / 100))
    return result<jupyter_output><empty_output><jupyter_text>On l’appelle ensuite en lui passant les paramètres :<jupyter_code>price = int(input("Quel est le prix d’origine ?"))
discount = int(input("Quelle est la réduction ?"))

result = sales(price, discount)

result = str(result)

print(f"Le prix soldé est de {result.replace('.', ',')} €.")<jupyter_output><empty_output><jupyter_text>Pour faciliter la lisibilité du code, on écrit des commentaires pour expliquer le rôle d’une fonction :<jupyter_code>def sales(price, discount):
    """Returns the discounted price of an item.

    Keywords arguments:
    price -- int : initial price of an item
    discount -- int : discount rate
    """
    result = price - (price * (discount / 100))
    return result<jupyter_output><empty_output><jupyter_text>**Note :** On aurait pu rajouter d’autres opérations au sein de la fonction, comme la conversion de `result` en `str`, mais il est plutôt conseillé de limiter les actions d’une fonction.  Tester un résultatAvant d’acheter un vêtement, on se demande si souvent si cela rentre dans notre budget. Rien de plus simple grâce aux tests qui permettent d’effectuer une comparaison entre plusieurs valeurs :<jupyter_code>budget = 50
price = int(input("Quel est le prix d’origine ?"))
discount = int(input("Quelle est la réduction ?"))

result = sales(price, discount)

if result < budget:
    print("La réduction vaut le coup, il faut en profiter !")
else:
    print("Malgré la réduction, ce n’est pas dans nos moyens.")<jupyter_output><empty_output><jupyter_text>On peut encore peaufiner les conditions en ajoutant des opérateurs logiques et d’autres conditions :<jupyter_code>budget = 50
ten_percent = budget + (10/100 * budget)
price = int(input("Quel est le prix d’origine ?"))
discount = int(input("Quelle est la réduction ?"))

result = sales(price, discount)

if result > budget and result <= ten_percent:
    print("Ça dépasse le budget de peu. Ce ne serait pas très raisonnable…")
elif result <= budget:
    print("La réduction vaut le coup, il faut en profiter !")
else:
    print("Malgré la réduction, ce n’est pas dans nos moyens.")<jupyter_output><empty_output><jupyter_text>Boucler sur un ensemble de données Les listesNotre programme n’aide vraiment à prendre de décision que pour l’achat d’un vêtement. Comment faire si l’on souhaite effectuer plusieurs achats ?Prenons le cas le plus simple où tous les articles bénéficient de la même réduction :<jupyter_code>discount = 15
# This variable is an instance of the "List" class
items = [32, 18, 21, 41]<jupyter_output><empty_output><jupyter_text>Une solution consisterait à obtenir le coût total des articles avant réduction, puis de lui appliquer la réduction :<jupyter_code>total = 0
# For an item in the list of items
for item in items:
    # Total is equal to itself + the cost of an item
    total = total + item

result = sales(total, discount)

print(result)<jupyter_output><empty_output><jupyter_text>Une autre solution consisterait à appliquer la réduction à chaque vêtement puis à additionner le résultat :<jupyter_code>result = 0
for item in items:
    result = result + sales(item, discount)

print(result)<jupyter_output><empty_output><jupyter_text>Bien que l’algorithme soit différent, le résultat demeure identique. Ce n’est pas surprenant : en informatique, il y a toujours plusieurs façons d’accomplir un objectif ! Regardez la solution suivante :<jupyter_code>print(sum([ sales(item, discount) for item in items ]))<jupyter_output><empty_output><jupyter_text>Quid du cas où les articles ne seraient pas tous soldés avec la même réduction ? On peut procéder avec deux listes, en s’assurant qu’elles soient de même longueur :<jupyter_code>items = [32, 18, 21, 41]
discounts = [15, 10, 25, 10]
if len(items) == len(discounts):
    print("Les deux listes ont le même nombre d’éléments.")<jupyter_output><empty_output><jupyter_text>Comme tout à l’heure, les solutions nécessitent de boucler sur la liste des items et, pour chacun d’eux, récupérer le taux de réduction correspondant.<jupyter_code>total = 0
for i, item in enumerate(items):
    result = sales(item, discounts[i])
    total = total + result
print(total)<jupyter_output><empty_output><jupyter_text>Que vient faire ce `i` ? `i` est une variable qui prend comme valeur, grâce à la fonction `enumerate()`, le rang de l’item courant dans la liste. Par exemple, l’item qui vaut `18` dans la liste est à la 2e position, juste derrière `32`. L’instruction `discounts[i]` va donc piocher la 2e valeur dans la liste `discounts`.  Les tuplesAfin d’éviter de manipuler deux listes, on peut tirer profit d’une autre structure de données : les tuples.<jupyter_code># A list of 4 tuples
items = [(32, 15), (18, 10), (21, 25), (41, 10)]
total = 0
for price, discount in items:
    result = sales(price, discount)
    total = total + result
print(total)<jupyter_output><empty_output><jupyter_text>Les dictionnairesEt encore une autre structure, plus explicite : les dictionnaires.<jupyter_code># A list of dicts
items = [
    {"price": 32, "discount": 15},
    {"price": 18, "discount": 10},
    {"price": 21, "discount": 25},
    {"price": 41, "discount": 10}
]
total = 0
for item in items:
    result = sales(item["price"], item["discount"])
    total = total + result
print(total)<jupyter_output><empty_output><jupyter_text>Lire un fichierEn pratique, les programmes informatiques servent à manipuler de grandes quantités de données. Comme il serait fastidieux d’obtenir toutes les données par des champs de formulaire à remplir, on opte pour la solution plus commode de les acquérir en lisant un fichier externe.<jupyter_code>with open("./data/sales.tsv") as file :
    # A list of lines
    lines = file.readlines()<jupyter_output><empty_output><jupyter_text>Plutôt concis ! En revanche, le résultat n’est pas immédiatement exploitable :<jupyter_code>print(lines)<jupyter_output><empty_output><jupyter_text>Pour nettoyer le résultat, on va d’abord supprimer la première ligne :<jupyter_code>lines = lines[1:]<jupyter_output><empty_output><jupyter_text>Puis, pour chaque ligne restante, supprimer le caractère `\n` (retour à la ligne) :<jupyter_code>for line in lines:
    line = line.replace('\n', '')<jupyter_output><empty_output><jupyter_text>Et enfin, utiliser le caractère `\t` (tabulation) comme séparateur entre le prix et la remise :<jupyter_code>for line in lines:
    line = line.replace('\n', '')
    values = line.split('\t')
    price = values[0]
    discount = values[1]<jupyter_output><empty_output><jupyter_text>Maintenant que le prix et la remise sont bien isolés, la fin du programme reste classique. N’hésitez pas à compléter le programme.Pour autant, il existe un moyen plus simple de traiter les données du fichier. `sales.tsv` est un fichier de type `tsv`, c’est-à-dire *tabulation-separated values*. Il est très similaire aux fichiers `csv`, mais au lieu d’utiliser la virgule comme caractère séparateur, on utilise une tabulation.Pour manipuler les fichiers `csv` et assimilés, on importe un module du même nom :<jupyter_code>import csv<jupyter_output><empty_output><jupyter_text>Ce module donne accès à des méthodes qui facilitent la manipulation du fichier :<jupyter_code>total = 0
with open('./data/sales.tsv') as file:
    reader = csv.DictReader(file, delimiter = '\t')
    for line in reader:
        result = sales(int(line['price']), int(line['discount']))
        total = total + result
print(total)<jupyter_output><empty_output><jupyter_text>Gestion des erreursExécutez le programme ci-dessous et saisissez dans la boîte de dialogue des caractères plutôt que des chiffres :<jupyter_code>price = int(input("Quel est le prix d’origine ?"))<jupyter_output><empty_output><jupyter_text>Python renvoie une erreur et nous indique que la valeur saisie est invalide. Plutôt que de laisser ce genre de messages imbuvables et qui met un terme brutal au programme, il est recommandé de gérer l’erreur :<jupyter_code>try:
    price = int(input("Quel est le prix d’origine ?"))
    discount = int(input("Quelle est la réduction ?"))
except ValueError:
    print("Veuillez saisir un nombre.")<jupyter_output><empty_output><jupyter_text>Et si l’on veut poser la question tant que l’utilisateur ne saisit pas une valeur correcte ?Il faut alors mettre en jeu deux mécanismes :- une boucle logique- un mécanisme de sortie de la boucle logique<jupyter_code>while 1:
    try:
        price = int(input("Quel est le prix d’origine ?"))
        discount = int(input("Quelle est la réduction ?"))
        break
    except ValueError:
        print("Veuillez saisir un nombre.")
print("Les valeurs saisies sont correctes !")<jupyter_output><empty_output><jupyter_text>Transmettre des arguments en ligne de commandeUne autre manière d’obtenir des informations de la part d’un utilisateur serait de les demander à l’exécution du script par la ligne de commande, comme dans :```shellls -l``` Le module `argparse` permet à un script de comprendre les arguments transmis par le système. Son utilisation nécessite trois étapes :1. instancier un objet de classe `ArgumentParser()`2. déclarer les arguments3. analyser (*parser*) les arguments<jupyter_code># import module
import argparse

# class instance
parser = argparse.ArgumentParser()

# statements
parser.add_argument("-p", "--price", type=int, help="price of the item")
parser.add_argument("-d", "--discount", type=int, help="discount to apply in percentage")

# parsing
args = parser.parse_args()<jupyter_output><empty_output><jupyter_text>Il est ensuite permis d’obtenir une aide à propos du script :<jupyter_code>!python ./scripts/sales.py --help<jupyter_output><empty_output><jupyter_text>Tout comme de lancer le script avec des valeurs déclarées :<jupyter_code>!python ./scripts/sales.py -p 89 -d 30<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09122949708553613, 'fraction_numerical': 0.008336722244814965, 'mean_word_length': 5.4124293785310735, 'pattern_counts': {'":': 8, '<': 152, '<?xml version=': 0, '>': 151, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '37', 'dir': 'jupyter-structured-clean-dedup', 'id': '274179', 'license': 'MIT', 'n_tokens_mistral': 4994, 'n_tokens_neox': 4863, 'n_words': 1744, 'path': '1.draft-first-program/1.a-little-taste-of-everything.ipynb', 'repo_name': 'mdjamina/python-M1TAL', 'size': '14754'}