Découvrir la programmation avec Python

Un cours complet pour novices

a marqué ce sujet comme résolu.

Malheureusement, ce tutoriel qui était en bêta a été supprimé par son auteur.

[Petite erreur, j'ai supprimé l'ancienne version et je n'ai pas l'URL de la nouvelle. ICI peut-être.]

Merci de vos relectures.

J'ai effectué un certain nombre de reprises. La plus importante concerne les parcours de listes. J'ai créé une sous-section

Boucle for : parcours d'une liste sans utiliser d'indices

et chaque fois que c'était possible (finalement assez souvent), j'ai utilisé cet idiome de parcours qui allège le code.

Toutefois ce procédé ne règle pas tous les problèmes. Je n'ai pas voulu utiliser l'idiome for i, elt in enumerate(L, start) car il donne une impression de complexité au débutant et m'entraînerait trop loin dans des explications. Mais surtout, je me suis rendu compte en récrivant certains codes que cet idiome ne règle pas certaines situations. Par exemple, j'avais le code suivant :

1
2
3
4
5
6
7
8
9
L = [10, 3, 12, 5]
maxi = L[0]
n = len(L)

for i, x in range(1, n):
    if L[i] > maxi:
        maxi = L[i]

print(maxi)

Le parcours n'a pas besoin de commencer à l'indice 0, il peut commencer à l'indice 1 puisque maxi réfère l'élément d'indice 0 que je ne vais bien entendu pas comparer à lui-même !!! Et c'est là que enumerate ne va pas suffire tel quel puisque enumerate(L) itère sur toute ma liste L. Bien sûr, je pourrais écrire une affreuseté comme :

1
2
3
for i, elt in enumerate():
   if i>0:
    # ETC

mais c'est un code complètement absurde : si vous avez une liste de 1 million d'éléments, vous allez tester que vous n'êtes pas au début de la liste une fois ce début passé (donc 999999 tests inutiles) ? (même un slice serait absurde car un slice ferait 999999 copies de références).

Chaque fois qu'un parcours d'une liste L va faire intervenir des conditions portant sur les indices (comme 3 indices après la position courante ou juste avant et juste après la position courante) utiliser enumerate est inapproprié.

Enfin, on trouve dans le code officiel Python de très nombreux usages de for i in range(len(L)), juste deux exemples sous Python3.4, dans mimetype.py :

1
2
3
4
            for i in range(len(words)):
                if words[i][0] == '#':
                    del words[i:]
                    break

ou dans copy.py

1
2
3
4
5
6
    for i in range(len(x)):
        if x[i] is not y[i]:
            y = tuple(y)
            break
    else:
        y = x

ça relativise ;)

J'envoie le tuto à la validation d'ici la fin de la journée.

+0 -0

Il y a bien cette solution :

1
2
3
4
5
iterator = iter(list)
maximum = next(iterator)
for element in iterator:
    if element > maximum:
        maximum = element

Mais en réalité ce n'est pas bien grave de commencer l'itération aussi sur le premier élément, on s'en fiche de faire un test inutile, c'est mieux que 99999 test inutile.

1
2
3
4
maximum = list[0]
for element in list:
    if element > maximum:
        maximum = element

oui, c'est une solution envisageable (mais qui dépasse de beaucoup le niveau attendu du tutoriel proposé) et qui n'est acceptable que parce qu'il y a juste un élément à ignorer. Mais imaginons qu'on veut juste parcourir la 2e moitié de la liste …

Autre situation non créée de toutes pièces : dans mon document, il y a un exercice où on demande la somme (mais on pourrait imaginer le produit) des éléments d'une liste en excluant les extrémités (indices 0 et n= len(L)). Une solution courante en Python serait de créer un slice L[1:-1] mais je trouve bien cher payé de faire une copie de n-2 références à cause de 2 objets placés à des endroits prévisibles.

C'est le prix de la simplicité, si on peut dire. En pratique on écrira sum(lst[1:-1]) parce que c'est simple et efficace. Mais si on a besoin de plus (ce qui est assez rare en vrai), on utilisera un autre type de donnée, avec numpy par exemple. Et si ce n'est toujours pas suffisant, on passera à un langage plus bas niveau.

Je n'ai pas le temps pour le moment de lire cette nouvelle version, alors je vais me baser uniquement sur les remarques que tu donnes dans ton premier post.

Je suis déjà content que tu adoptes les boucles for simple pour parcourir tes listes, qui sont à mon avis plus simples à lire.

Je comprends tes réticences quant à enumerate, qui complexifie la structure, mais que je ne trouve pas plus compliqué à appréhender qu'un range(len(L)) suivi d'un L[i].

Tu évoques par ailleurs des soucis de performances avec un test qui serait répété 999999 fois. Mais les performances à ce niveau sont largement négligeables, et pas forcément moins bonnes qu'une itération à base de range et d'un accès par index à chaque tour.

Pour ce qui est des copies, elles sont négligeables aussi dans des cas élémentaires. Et d'autres solutions viendront les remplacer dans les cas critiques : soit par islice du module itertools, soit par numpy comme l'évoque psycopy.
Je ne pense pas que la question des performances (à un niveau aussi minime) doit être prise en compte pour choisir les directions pédagogiques.

Tes exemples sont des exemples pour débutants qui ne sont utiles que pour l'apprentissage, et qui ne seront jamais mis en œuvre dans un vrai programme. Quand on cherche la maximum d'un ensemble d'éléments, on utilise la fonction max. Donc je ne vois pas l'utilité de chercher à présenter le calcul de maximum le plus performant possible, alors qu'il sera dans tous les cas inférieur à max.

Enfin, pour ton exemple tiré de mimetype.py, la liste est modifiée durant l'itération, ce qui est un cas où le simple for element in iterable n'est pas suffisant.

Ce sujet est verrouillé.