fonctions Turtle

Identification

Infoforall

7 - Une fonction une tâche


Nous allons maintenant utiliser la puissance des fonctions pour réaliser des choses sympathiques avec le module Turtle.

Vous allez également voir qu'il faut tenter d'associer chaque tâche avec une fonction dans vos programmes. Et on pourra les emboîter les unes dans les autres.

Logiciel nécessaire pour l'activité : Python 3

Evaluation ✎ : question 10-11-12-13-14-15

1 - Fonction nouveau stylo

Nous allons commencer par créer une fonction qui va nous permettre de créer un stylo en définissant sa couleur d'écriture, sa couleur de fond et sa largeur.

1 2 3 4 5 6 7 8 9 10
import turtle as trt def nouveau_stylo(ecriture, fond, largeur) : '''Renvoie la référence d'un stylo configuré''' feutre = trt.Turtle() feutre.color(ecriture) feutre.fillcolor(fond) feutre.pensize(largeur) feutre.speed(5) return feutre

01° Que renvoie cette fonction ?

  1. Un dessin
  2. La référence d'une "tortue" (Turtle) qui nous servira de crayon
  3. La couleur du crayon
  4. La réference d'un dessin

...CORRECTION...

Ligne 5, on récupère et stocke la référence d'un objet Turtle(tortue) : feutre = trt.Turtle().

  • Lignes 6 à 9 : on configure cet objet d'écriture.
  • Ligne 10 : on renvoie feutre et donc la référence de l'objet Turtle d'écriture.
  • 02° Mettre le code en mémoire. Utiliser la fonction en utilisant le code suivant par exemple :

    >>> s1 = nouveau_stylo("red","black", 4) >>> s1.forward(150) >>> s1.left(120) >>> s1.forward(150) >>> s1.left(120) >>> s1.forward(150) >>> s1.left(120)

    2 - Déplacer le crayon avec une fonction

    Voici maintenant une fonction deplacer qui permet de modifier la position du crayon. Si vous vous souvenez, c'est assez pénible à taper à la main :

    • Il faut soulever le crayon
    • Déplacer le crayon
    • Abaisser le crayon pour pouvoir dessiner à nouveau

    Il ne s'agit que d'une fonction gérant l'affichage, elle ne calcule rien et ne renvoie rien. Sa tâche consiste juste à modifier l'interface graphique.

    Voici le nouveau code. J'ai rajouté la documentation cette fois.

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
    import turtle as trt def nouveau_stylo(ecriture, fond, largeur) : '''Renvoie la référence d'un stylo configuré :: param ecriture(str) :: une couleur autorisée (pour l'écriture) :: param fond(str) :: une couleur autorisée (pour le fond éventuel) :: param largeur(int) :: la largeur d'écriture :: return (Turtle) :: la référence du crayon ''' feutre = trt.Turtle() feutre.color(ecriture) feutre.fillcolor(fond) feutre.pensize(largeur) feutre.speed(5) return feutre def deplacer(feutre, x, y) : '''Lève le feutre, déplace le feutre et abaisse le feutre :: param feutre(Turtle) :: la référence du crayon :: param x(int) :: coordonnée horizontale (abscisse) :: param y(int) :: coordonnée verticale (ordonnée) :: return (None) :: c'est une fonction sans retour ''' feutre.penup() # On lève la pointe feutre.setpos(x, y) # On déplace le crayon feutre.pendown() # On abaisse la pointe

    03° Mettre le code en mémoire. Utiliser ce code qui va vous permettre de tracer trois traits.

    >>> s1 = nouveau_stylo("red","black", 4) >>> s1.forward(150) >>> deplacer(s1, 0, 0) >>> s1.left(120) >>> s1.forward(150) >>> deplacer(s1, 0, 0) >>> s1.left(120) >>> s1.forward(150)

    04° Quelqu'un dit que la fonction n'est pas complète : la documentation dit que la fonction renvoie None alors qu'elle ne renvoie rien pour l'instant. Il propose de rajouter une ligne à la fonction.

    19 20 21 22 23 24 25 26 27 28 29 30 31
    def deplacer(feutre, x, y) : '''Lève le feutre, déplace le feutre et abaisse le feutre :: param feutre(Turtle) :: la référence du crayon :: param x(int) :: coordonnée horizontale (abscisse) :: param y(int) :: coordonnée verticale (ordonnée) :: return (None) :: c'est une fonction sans retour ''' feutre.penup() # On lève la pointe feutre.setpos(x, y) # On déplace le crayon feutre.pendown() # On abaisse la pointe return None

    Question : cette modification est-elle effectivement obligatoire ?

    ...CORRECTION...

    Absolument pas.

    Si on ne signale aucun retour, Python "rajoutera" return None automatiquement en réalité.

    Modifier l'état par effet de bord

    Comme on peut le voir, cette fonction modifie l'état du "crayon" puisqu'il est déplacé.

    Sans retour.

    Comment ? Aucune affectation si vous observez bien. On agit juste sur le crayon à travers sa référence (feutre) qu'on a transmis en paramètre.

    On parlera de modification d'état par effet de bord pour caractériser ce genre de comportement.

    Nous verrons avec le cours sur les tableaux comment cela fonctionne.

    Du coup, il faudrait l'indiquer dans la documentation :

    19 20 21 22 23 24 25 26 27 28 29 30 31
    def deplacer(feutre, x, y) : '''Lève le feutre, déplace le feutre et abaisse le feutre :: param feutre(Turtle) :: la référence du crayon :: param x(int) :: coordonnée horizontale (abscisse) :: param y(int) :: coordonnée verticale (ordonnée) :: return (None) :: c'est une fonction sans retour .. effet de bord :: modifie l'état de feutre ''' feutre.penup() # On lève la pointe feutre.setpos(x, y) # On déplace le crayon feutre.pendown() # On abaisse la pointe

    3 - Une fonction pour chaque tâche

    Nous allons voir ici quelque chose de fondamental au niveau de la conception des fonctions : chaque fonction de base ne doit normalement réaliser qu'une une tâche basique.

    Si on a besoin de réaliser une tâche complexe comment faire alors ?

    Réponse : en créant une fonction qui va faire appel aux fonctions de base précédentes.

    On pourrait résumer cela à : une fonction pour une tâche.

    05° Compléter la fonction trois de façon à ce qu'elle traçe un trait, tourne de l'angle, traçe un trait, tourne de l'angle, traçe un trait et tourne de l'angle.

    Le code à compléter se trouve ci-dessous. Travaillez uniquement sur trois pour l'instant.

    Vous pourrez vous inspirer de la question 03. Attention : le crayon-turtle ne se nomme pas s1 ici ! Lisez la documentation.

    ...CORRECTION...

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
    def trois(feutre, distance, angle) : '''Fait avancer le crayon de la distance, tourne de l'angle donné. Trois fois. :: param feutre(Turtle) :: la référence du crayon :: param distance(int) :: la distance à parcourir :: param angle(int) :: l'angle de rotation en degrés :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de feutre ''' feutre.forward(distance) feutre.left(angle) feutre.forward(distance) feutre.left(angle) feutre.forward(distance) feutre.left(angle)
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
    import turtle as trt def nouveau_stylo(ecriture, fond, largeur) : '''Renvoie la référence d'un stylo configuré :: param ecriture(str) :: une couleur autorisée (pour l'écriture) :: param fond(str) :: une couleur autorisée (pour le fond éventuel) :: param largeur(int) :: la largeur d'écriture :: return (Turtle) :: la référence du crayon ''' feutre = trt.Turtle() feutre.color(ecriture) feutre.fillcolor(fond) feutre.pensize(largeur) feutre.speed(5) return feutre def deplacer(feutre, x, y) : '''Lève le feutre, déplace le feutre et abaisse le feutre :: param feutre(Turtle) :: la référence du crayon :: param x(int) :: coordonnée horizontale (abscisse) :: param y(int) :: coordonnée verticale (ordonnée) :: return (None) :: c'est une fonction sans retour .. effet de bord :: modifie l'état de feutre ''' feutre.penup() # On lève la pointe feutre.setpos(x, y) # On déplace le crayon feutre.pendown() # On abaisse la pointe def trois(feutre, distance, angle) : '''Fait avancer le crayon de la distance, tourne de l'angle donné. Trois fois. :: param feutre(Turtle) :: la référence du crayon :: param distance(int) :: la distance à parcourir :: param angle(int) :: l'angle de rotation en degrés :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de feutre ''' pass def triangle(ftr, cote) : '''Trace un triangle (equilatéral) à l'aide du crayon f :: param ftr(Turtle) :: la référence du crayon :: param cote(int) :: la valeur en pixel des côtés :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de ftr ''' pass def trace_triangle(ecrit, fd, epaisseur, cx, cy, c) : '''Trace un triangle (equilatéral) en (x, y) dans l'application graphique :: param ecrit(str) :: une couleur autorisée (pour l'écriture) :: param fd(str) :: une couleur autorisée (pour le fond éventuel) :: param epaisseur(int) :: la largeur d'écriture :: param cx(int) :: coordonnée horizontale (abscisse) :: param cy(int) :: coordonnée verticale (ordonnée) :: param c(int) :: la valeur en pixel des côtés :: return (Turtle) :: renvoie la référence du crayon ayant servi à dessiner ''' pass

    06° Rajouter cette fonction en mémoire : elle permet de dessiner un triangle en utilsant ... trois.

    1 2 3 4 5 6 7 8 9 10
    def triangle(ftr, cote) : '''Trace un triangle (equilatéral) à l'aide du crayon f :: param ftr(Turtle) :: la référence du crayon :: param cote(int) :: la valeur en pixel des côtés :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de ftr ''' trois(ftr, cote, 120)

    Tester ce code dans la console : il permet de tracer un triangle.

    >>> s1 = nouveau_stylo("green","black", 4) >>> triangle(s1, 150)

    07° Répondre aux questions suivantes qui sont liées à ces instructions :

    >>> s1 = nouveau_stylo("green","black", 4) >>> triangle(s1, 150)
    1. Appel de triangle dans la console : que contiennent les paramètres ftr et cote lorsqu'on lance triangle(s1, 150) ?
    2. Appel de trois : que contiennent alors les paramètres feutre, distance et angle lorsqu'on lance trois(ftr, cote, 120) ?

    ...CORRECTION...

    On lance triangle(s1, 150).

    Le prototype de cette fonction est def triangle(ftr, cote).

    On voit donc que ftr reçoit la référence de s1.

    On voit que cote reçoit la valeur 150.

    Ensuite, on lance trois(ftr, cote, 120).

    Le prototype de la fonction est def trois(feutre, distance, angle).

    feutre fait donc référence à ftr et donc s1, le crayon du départ !

    distance fait donc référence à cote et donc 150.

    angle fait donc référence à 120.

    On trace donc un triangle (car trois traits avec un angle de 120°) avec le crayon du départ.

    Comme on peut le voir, on utilise des fonctions basiques et des fonctions plus complexes qui utilsent d'autres fonctions.

    08° Compléter maintenant la fonction trace_triangle qui doit :

    1. Créer un nouveau stylo f (en utilisant nouveau_stylo)
    2. Déplacer ce stylo en (x,y) (en utilisant deplacer)
    3. Dessiner un triangle à cet endroit (en utilisant triangle)
    4. Renvoyer la référence f

    Tester votre fonction en utilisant ceci par exemple :

    >>> trace_triangle("blue", "yellow", 4, 50, 50, 200)

    ...CORRECTION...

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
    def trace_triangle(ecrit, fd, epaisseur, cx, cy, c) : '''Trace un triangle (equilatéral) en (x, y) dans l'application graphique :: param ecrit(str) :: une couleur autorisée (pour l'écriture) :: param fd(str) :: une couleur autorisée (pour le fond éventuel) :: param epaisseur(int) :: la largeur d'écriture :: param cx(int) :: coordonnée horizontale (abscisse) :: param cy(int) :: coordonnée verticale (ordonnée) :: param c(int) :: la valeur en pixel des côtés :: return (Turtle) :: renvoie la référence du crayon ayant servi à dessiner ''' f = nouveau_stylo(ecrit, fd, epaisseur) deplacer(f, cx, cy) triangle(f, c) return f

    Comme vous l'avez vu, nous sommes parvenu à réaliser cette action assez complexe en la décomposant en de multiples petites fonctions. C'est une manière de faire qu'il faudra prendre l'habitude d'utiliser.

    L'intérêt ?

    • Lors de l'élaboration, on peut juste réfléchir à ce que doivent faire les fonctions. On pourra alors répartir le travail entre différentes personnes.
    • Si on veut intervenir sur une partie du code, il est plus facile de la localiser et de ne pas toucher le reste du code.
    • Si on veut réaliser une action proche mais pas identique, on a de grandes chances de pouvoir réutiliser une partie des fonctions : ça éviter de coder des choses qui sont déjà réalisées !

    Imaginons que je veuille maintenant rajouter un fond et cacher le crayon. Il me suffit de modifier la fonction trace_triangle :

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
    def trace_triangle(ecrit, fd, epaisseur, cx, cy, c) : '''Trace un triangle (equilatéral) en (x, y) dans l'application graphique :: param ecrit(str) :: une couleur autorisée (pour l'écriture) :: param fd(str) :: une couleur autorisée (pour le fond éventuel) :: param epaisseur(int) :: la largeur d'écriture :: param cx(int) :: coordonnée horizontale (abscisse) :: param cy(int) :: coordonnée verticale (ordonnée) :: param c(int) :: la valeur en pixel des côtés :: return (Turtle) :: renvoie la référence du crayon ayant servi à dessiner ''' f = nouveau_stylo(ecrit, fd, epaisseur) deplacer(f, cx, cy) f.begin_fill() triangle(f, c) f.end_fill() f.hideturtle() return f

    Ou alors, je trouve pénible de devoir toujours donner les couleurs alors que je ne veux que du bleu à fond orange :

    1 2 3 4 5 6 7 8 9 10
    def rapide_triangle(cx, cy, c) : '''Trace un triangle (equilatéral) en (x, y) dans l'application graphique :: param cx(int) :: coordonnée horizontale (abscisse) :: param cy(int) :: coordonnée verticale (ordonnée) :: param c(int) :: la valeur en pixel des côtés :: return (Turtle) :: renvoie la référence du crayon ayant servi à dessiner ''' return trace_triangle("blue", "orange", 3, cx, cy, c)

    09° Placer les deux dernières fonctions en mémoire également.

    Tester avec des commandes comme :

    >>> t1 = rapide_triangle(0,0,100) >>> t2 = rapide_triangle(50,90,100) >>> t3 = rapide_triangle(-50,90,100)

    Vous devriez obtenir ceci :

    ✎ 10° Compléter la fonction dessin1 pour que son appel provoque le dessin précédent.

    1 2 3 4 5 6 7
    def dessin1() : '''Réalise le dessin voulu :: return (None) :: Aucun retour ''' pass

    Comme on peut le voir, créer des fonctions basiques qui serviront à d'autres fonctions peut au final créer des fonctions très complexes.

    Voici pour finir une illustration de ce princpe

    ✎ 11° Compléter la fonction quatre de façon à ce qu'elle traçe un trait, tourne de l'angle, traçe un trait, tourne de l'angle, traçe un trait, tourne de l'angle, traçe un trait et tourne de l'angle. Bref, 4 fois.

    1 2 3 4 5 6 7 8 9 10
    def quatre(feutre, distance, angle) : '''Fait avancer le crayon de la distance, tourne de l'angle donné. Quatre fois. :: param feutre(Turtle) :: la référence du crayon :: param distance(int) :: la distance à parcourir :: param angle(int) :: l'angle de rotation en degrés :: return (None) :: fonction sans retour ''' pass

    Si vous faites un appel avec un angle de 90°, elle doit donc vous tracer un carré.

    >>> s1 = nouveau_stylo("green","black", 4) >>> quatre(s1, 150, 90)

    ✎ 12° Compléter la fonction carre de façon à ce qu'elle traçe un carré en utisant la fonction quatre.

    1 2 3 4 5 6 7 8 9 10
    def carre(ftr, cote) : '''Trace un carre à l'aide du crayon f :: param ftr(Turtle) :: la référence du crayon :: param cote(int) :: la valeur en pixel des côtés :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de ftr ''' pass

    Si vous faites un appel, elle doit donc vous tracer un carré.

    >>> s1 = nouveau_stylo("green","black", 4) >>> carre(s1, 150)

    Une fois cette fois créée, on pourrait l'utiliser pour totalement encapsuler la réalisation d'un carré par exemple :

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
    def trace_carre(ecrit, fd, epaisseur, cx, cy, c) : '''Trace un carré en (x, y) dans l'application graphique :: param ecrit(str) :: une couleur autorisée (pour l'écriture) :: param fd(str) :: une couleur autorisée (pour le fond éventuel) :: param epaisseur(int) :: la largeur d'écriture :: param cx(int) :: coordonnée horizontale (abscisse) :: param cy(int) :: coordonnée verticale (ordonnée) :: param c(int) :: la valeur en pixel des côtés :: return (Turtle) :: renvoie la référence du crayon ayant servi à dessiner ''' f = nouveau_stylo(ecrit, fd, epaisseur) deplacer(f, cx, cy) f.begin_fill() carre(f, c) f.end_fill() f.hideturtle() return f

    ✎ 13° En appliquant le principe d'une fonction par tâche, quelle est la seule ligne différente entre les fonctions trace_carre et trace_triangle ?

    ✎ 14° Compléter la fonction tour pour que son appel provoque le dessin proposé.

    1 2 3 4 5 6 7
    def tour() : '''Réalise le dessin voulu :: return (None) :: Aucun retour ''' pass

    Nous pourrions continuer et refaire le même cheminement pour créer des disques colorés. Voici de quoi réaliser ce type de dessin. Cette fois, j'ai crée les fonctions arc_de_cercle, cercle et trace_disque.

    Exemple d'utilisation :

    >>> trace_disque("red", "orange", 10, 0, 0, 100) <turtle.Turtle object at 0x7ff58c7d9be0>

    Résultat :

    Le code à rajouter (si vous le voulez):

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
    def arc_de_cercle(feutre, rayon, angle) : '''Trace l'arc de cercle voulu. :: param feutre(Turtle) :: la référence du crayon :: param rayon(int) :: le rayon de l'arc de cercle :: param angle(int) :: l'angle de rotation en degrés :: return (None) :: fonction sans retour ''' feutre.circle(rayon, angle) def cercle(ftr, rayon) : '''Trace un cercle à l'aide du crayon f :: param ftr(Turtle) :: la référence du crayon :: param rayon(int) :: la valeur en pixels du rayon :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de ftr ''' arc_de_cercle(ftr, rayon, 360) def trace_disque(ecrit, fd, epaisseur, cx, cy, r) : '''Trace un disque coloré en (x, y) dans l'application graphique :: param ecrit(str) :: une couleur autorisée (pour l'écriture) :: param fd(str) :: une couleur autorisée (pour le fond éventuel) :: param epaisseur(int) :: la largeur d'écriture :: param cx(int) :: coordonnée horizontale (abscisse) :: param cy(int) :: coordonnée verticale (ordonnée) :: param r(int) :: la valeur en pixels du rayon :: return (Turtle) :: renvoie la référence du crayon ayant servi à dessiner ''' f = nouveau_stylo(ecrit, fd, epaisseur) deplacer(f, cx, cy) f.begin_fill() cercle(f, r) f.end_fill() f.hideturtle() return f

    ✎ 15° Pourquoi la fonction renvoie-t-elle l'adresse du crayon ayant servi à tracer le disque ?

    >>> trace_disque("red", "orange", 10, 0, 0, 100) <turtle.Turtle object at 0x7ff58c7d9be0>

    4 - FAQ

    Et left, forward... Ce sont des fonctions aussi non ?

    Comme vous avez dû le remarquer, on peut agir sur le crayon / feutre en utilsant des sortes de fonctions : left, forward...

    La syntaxe est néanmoins différentes de celles des fonctions : on doit placer le nom de l'objet sur lequel on agit, placer un point puis placer le nom de la fonction.

    1 2
    feutre.forward(distance) feutre.left(angle)

    Si vous regardez bien, vous pourrez d'ailleurs voir qu'elles sont en rouge foncé, contrairement aux fonctions qui sont en rouge clair.

    Pourquoi ? Tout simplement car ces fonctions portent un nom particulier.

    On les nomme des méthodes : ce sont des fonctions incorporées de base à l'intérieur de certaines structures qu'on nomme des objets. Comme notre Turtle.

    Si vous regardez les codes Python que vous pouvez trouver sur le Web, vous risquez de tomber assez régulièrement sur ces drôles d'appels de fonctions avec le point.

    En résumé, il s'agit juste de fonctions incorporées et on les nomme méthodes.

    La codification est donc : objet.methode().

    Il faut donc veiller à ce qu'une fonction ne soit conçue que pour répondre à UNE tâche.

    On retiendra donc que pour réaliser une tâĉhe un peu complexe, il faut :

    • Identifier et isoler les tâches basiques dans des fonctions
    • Réaliser les tâches "moyennes" à l'aide de fonctions qui utilisent les tâches basiques
    • Réaliser les tâches complexes en utilisant les fonctions basiques et moyennes précédentes

    Une sorte d'emboîtement.

    Activité publiée le 01 11 2020
    Dernière modification : 01 11 2020
    Auteur : ows. h.