Alternative au do…while en Python


De nombreuses instructions ont été volontairement écartées de Python. Le goto bien entendu, mais aussi le switch, unless et le do...while.

Le but est de limiter le nombre de mots clés à connaitre afin de comprendre le langage. Les créateurs ont choisi donc de mettre de côté des mots clés trop souvent mal utilisés, pas assez utilisés, ou qui possèdent des alternatives suffisantes.

La boucle while est rarement utilisée en Python, en tout cas beaucoup, beaucoup moins que sa petite soeur la boucle for. Avoir besoin d’un do...while est encore plus rare, et donc ne peut faire partie du club très fermé des mots clés réservés.

Si l’on souhaite obtenir l’effet du do..while en python, on fait donc généralement une boucle infinie suivie d’un break sur une condition. Exemple:

import random
 
choix = random.randint(0, 100)
 
while True:
    reponse = int(input('Devinez le nombre: '))
    if reponse < choix:
        print('Plus grand')
    elif reponse > choix:
        print('Plus petit')
    else:
        break
print('Bravo')

Le défaut de cette technique est qu’elle ne rend pas clair dès le début la condition de sortie de la boucle. Aujourd’hui en parcourant la mailling list python-idea, je suis tombé sur une idée pas conne:

import random
 
choix = random.randint(0, 100)
 
while "L'utilisateur n'a pas encore deviné le nombre":
    reponse = int(input('Devinez le nombre: '))
    if reponse < choix:
        print('Plus grand')
    elif reponse > choix:
        print('Plus petit')
    else:
        break
print('Bravo')

Ca marche car les chaînes non vides sont toujours vraies en Python, et ça documente le code :)

21 thoughts on “Alternative au do…while en Python

  • Poisson

    Ça marche, mais ça va pas apporter de la confusion? La conversion en booléan des différents types est pas forcément super connu j’ai l’impression..

  • Sam Post author

    Il n’y a pas de conversion en booléen, mais toutes les valeurs Python ont une valeur dans un contexte de logique booléenne. Cela fait partie de la base de connaissance du langage. On peut bien entendu ne pas le savoir. On a tous le droit d’être débutant, mais on ne peut pas demander d’utiliser le langage uniquement partiellement pour que les débutant comprennent toujours tout.

  • dineptus

    Perso j’ai plutôt tendance à faire ca en fait :

    import random

    choix = random.randint(0, 100)

    reponse = None

    while reponse != choix:

    reponse = int(input('Devinez le nombre: '))

    if reponse < choix:

    print('Plus grand')

    elif reponse > choix:

    print('Plus petit')

    print('Bravo')

    Ca a l’avantage d’être explicite et concis, pas besoin de break (ce qui peut être un avantage en cas de récursion multiple) et facile à comprendre pour tous.

    A l’inverse je préfère les boucles infinies avec break quand les conditions d’arrêt ne sont pas super simples (genre arrêter en cas d’une exemption particulière par exemple).

    Je ne vois que peu de cas d’usage de do ... while qui ne sont pas remplacables par un exemple comme le mien.

  • buffalo974

    Le match de Rust serait interessant à pythoniser.

    println!(“Tell me about {}”, number);

    match number {

    // Match a single value

    1 => println!(“One!”),

        // Match several values
        2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
    
        // Match an inclusive range
        13...19 => println!("A teen"),
    
        // Handle the rest of cases
        _ => println!("Ain't special"),
    }
    

    https://rustbyexample.com/flow_control/match.html

  • Romain

    Ça me rappelle un échange récent entre Hettinger et Beazley, la premier ayant fini par proposer, taquin, l’utilisation de :

    while "False":

    Sur le principe je trouve ça fun, mais en même temps je trouve que c’est un peu détourner la propriété des chaînes non vides à être toujours vraie. Ça ne me semble pas très explicite.

  • Alerion

    Dans le premier exemple, la ligne :

    choix = random.randint(0, 1)

    Ce ne serait pas plutôt randint(0, 100) ?

  • Abjects

    Une typo : “Le défaut de cette technique et qu’elle” -> “Le défaut de cette technique est qu’elle

  • Sam Post author

    @dineptus: je suis assez d’accord. C’est plus une réponse à une question qu’on me pose souvent.

    @buffalo974:

    On ne gagne pas grand chose. Une syntaxe spéciale pour faire ce qu’on peut déjà aire avec des builts in, c’est un peu overkill:

    if x in (2, 3, 5, 7, 11): print("This is a prime")
    
    elif x in range(13, 19):  print("A teen")
    
    else: print("Ain't special")
    

    @Alerion : oui :)

    @Abjects : anéfé

  • Laurent Pointal

    Hum, il y a bien une évaluation Vrai/Faux de la chaîne donc un transtypage (c’est un des pièges de Python que je regrette pour les débutants, on voit écrire dans certains exercices: if v == ('o' or 'n')).

    Dans ce cas je préfère un while True:, pour lequel tout développeur doit se dire qu’il y aura un break dans le corps de la boucle.

    Ou sinon quelque chose comme:

    continuer = True

    while continuer:

    … blabla

    continuer = expression qu'on mettrais dans le do while

    D’ailleurs, ça peut facilement s’adapter en répéter jusqu’à.

  • francoisb

    J’utilise toujours la méthode de dineptus (comme lui, j’évite le break pour les cas simples). Pour le do ... until..., on fait::

    continuer = True

    while continuer:

    . . . . contenu de la boucle

    . . . . continuer = not(condition d'arrêt)

    sortie de boucle

  • dineptus

    Question un peu HS mais il y aurait pas moyen d’activer markdown au lieu de HTML pour les commentaires ? A chaque fois j’oublie d’ajouter pre avant de code et la mise en page se casse :(

    Je pourrais aussi mieux regarder la preview à la place donc c’est en grande partie ma faute j’en suis conscient, et désolé ;)

  • Sam Post author

    @Laurent Pointal : pas de transtypage, car il n’y a pas de conversion de type. C’est du duck typing. On peut l’implémenter soit même en créant une méthode bool sur ses objets.

    @dineptus : faudrait rajouter un plugin wordpress mais c’est sans doute possible.

  • Marc

    Personnellement, je n’aime pas du tout cette pratique, j’ai tendance à éviter le break autant que possible car ça oblige à refaire globalement l’algorithme du bloc dans sa tête pour comprendre le fonctionnement, et puis ce n’est pas joli syntaxiquement. Je préfère la version où la condition de boucle a un sens logique. Sinon je me sers souvent d’un truc de Python qui ne sert à rien mais qui indique “humainement” ce que je veux faire lorsque je fais une boucle do…while, c’est Ellipsis (qui vaut True en valeur booléenne).

        data = ...

        while data:

            url = r'{base_url}?page={page}'

            data = session.get(url).json()

            for element in data:

                do_something(element)

            page += 1

  • Laurent Pointal

    @SAM

    Sur le transtypage,

    def f(x):
    ____if x:
    ________print(x)
    

    Désassemblé:

    >>> dis.dis(f)
      2           0 LOAD_FAST                0 (x)
                  2 POP_JUMP_IF_FALSE       12
    
      3           4 LOAD_GLOBAL              0 (print)
                  6 LOAD_FAST                0 (x)
                  8 CALL_FUNCTION            1
                 10 POP_TOP
            >>   12 LOAD_CONST               0 (None)
                 14 RETURN_VALUE
    
    

    Il n’y a pas de récupération de la valeur booléenne, mais une évaluation de la valeur en tant que booléen… est-ce que ça peut être considéré comme un transtypage (implicite) ou non…

  • OPi

    Attention, scoop : Python permet de commenter le code ! ;-)

    while True: # L'utilisateur n'a pas encore deviné le nombre

  • kontre

    @OPi: Dans ce cas là il faut 2 espaces avant le # pour les commentaires en fin de ligne. pep8 rocks ! :D

  • utopman

    Quand j’ai vu ce post, j’ai fini par me dire que si personne n’a benché j’allais le faire.

    Je vous propose ce code pour tester qui selon moi fait l’affaire, mais j’ai pu négliger quelque chose

    Les résultats me surprennent. Chez moi le test booleen prend plus de temps et ce n’est pas négligeable. Par ailleurs entre python 2.7 et 3.5 il y a aussi de gros écarts : ~ +60% de temps pour python 3.

    Heureusement, je ne fais pas tous les jours des boucles de while.

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.