Serveur d'impression

Votre guide de la fonction Python print () – Real Python – Bien choisir son serveur d impression

Le 25 mars 2021 - 96 minutes de lecture


Si vous êtes comme la plupart des utilisateurs de Python, moi y compris, vous avez probablement commencé votre aventure Python en vous renseignant sur impression(). Cela vous a aidé à écrire le vôtre Bonjour le monde bon mot. Vous pouvez l'utiliser pour afficher des messages formatés à l'écran et peut-être trouver des bogues. Mais si vous pensez que c'est tout ce qu'il y a à savoir sur Python impression() fonction, alors vous manquez beaucoup de choses!

Continuez à lire pour profiter pleinement de cette petite fonction apparemment ennuyeuse et méconnue. Ce tutoriel vous permettra de vous familiariser avec l'utilisation de Python impression() effectivement. Cependant, préparez-vous à une plongée approfondie en parcourant les sections. Vous serez peut-être surpris de voir à quel point impression() a à offrir!

Si vous êtes un débutant complet, vous bénéficierez le plus de la lecture de la première partie de ce didacticiel, qui illustre les principes de base de l'impression en Python. Sinon, n'hésitez pas à sauter cette partie et à sauter comme bon vous semble.

L'impression en bref

Commençons par examiner quelques exemples concrets d’impression en Python. À la fin de cette section, vous connaîtrez tous les modes d'appels possibles impression(). Ou, dans le jargon du programmeur, vous diriez que vous serez familier avec le signature de fonction.

Appel impression()

L'exemple le plus simple d'utilisation de Python impression() ne nécessite que quelques touches:

Vous ne passez aucun argument, mais vous devez tout de même mettre des parenthèses vides à la fin, ce qui indique à Python d'exécuter réellement la fonction plutôt que de simplement y faire référence par son nom.

Cela produira un caractère de nouvelle ligne invisible, qui à son tour provoquera l'apparition d'une ligne vide sur votre écran. Tu peux appeler impression() plusieurs fois comme ceci pour ajouter un espace vertical. C'est comme si tu frappais Entrer sur votre clavier dans un traitement de texte.

UNE caractère de nouvelle ligne est un caractère de contrôle spécial utilisé pour indiquer la fin d'une ligne (EOL). Il n’a généralement pas de représentation visible à l’écran, mais certains éditeurs de texte peuvent afficher ces caractères non imprimables avec de petits graphiques.

Le mot «caractère» est quelque peu inapproprié dans ce cas, car une nouvelle ligne comporte souvent plus d'un caractère. Par exemple, le système d'exploitation Windows, ainsi que le protocole HTTP, représentent les retours à la ligne avec une paire de caractères. Parfois, vous devez tenir compte de ces différences pour concevoir des programmes vraiment portables.

Pour découvrir ce qui constitue une nouvelle ligne dans votre système d’exploitation, utilisez la fonction intégrée de Python os module.

Cela vous dira immédiatement que les fenêtres et DOS représentent la nouvelle ligne comme une séquence de r suivie par n:

>>>

>>> importer os
>>> os.pas de ligne
' r  n'

Sur Unix, Linux, et les versions récentes de macOS, c’est un single n personnage:

>>>

>>> importer os
>>> os.pas de ligne
' n'

Le classique Mac OS X, cependant, s'en tient à sa propre philosophie de «penser différemment» en choisissant encore une autre représentation:

>>>

>>> importer os
>>> os.pas de ligne
' r'

Remarquez comment ces caractères apparaissent dans les littéraux de chaîne. Ils utilisent une syntaxe spéciale avec une barre oblique inverse () pour désigner le début d'un séquence de caractères d'échappement. De telles séquences permettent de représenter des caractères de contrôle, qui seraient autrement invisibles à l'écran.

La plupart des langages de programmation sont livrés avec un ensemble prédéfini de séquences d'échappement pour les caractères spéciaux tels que ceux-ci:

  • \: barre oblique inverse
  • b: retour arrière
  • t: languette
  • r: retour chariot (CR)
  • n: nouvelle ligne, également appelée saut de ligne (LF)

Les deux derniers rappellent les machines à écrire mécaniques, qui nécessitaient deux commandes distinctes pour insérer une nouvelle ligne. La première commande ramènerait le chariot au début de la ligne courante, tandis que la seconde ferait avancer le rouleau jusqu'à la ligne suivante.

En comparant les Codes de caractères ASCII, vous verrez que placer une barre oblique inverse devant un caractère change complètement sa signification. Cependant, tous les caractères ne le permettent pas, seuls les caractères spéciaux.

Pour comparer les codes de caractères ASCII, vous pouvez utiliser la fonction intégrée ord () fonction:

>>>

>>> ord(«r»)
114
>>> ord(' r')
13

Gardez à l'esprit que, pour former une séquence d'échappement correcte, il ne doit y avoir aucun espace entre la barre oblique inverse et une lettre!

Comme tu viens de le voir, appeler impression() sans argument entraîne un ligne blanche, qui est une ligne composée uniquement du caractère de nouvelle ligne. Ne confondez pas cela avec un ligne vide, qui ne contient aucun caractère, pas même la nouvelle ligne!

Vous pouvez utiliser les chaînes littérales de Python pour visualiser ces deux éléments:

' n'  # Ligne blanche
''    # Ligne vide

Le premier a un caractère, tandis que le second n'a pas de contenu.

Dans un scénario plus courant, vous souhaitez communiquer un message à l'utilisateur final. Il existe plusieurs moyens d'y parvenir.

Tout d'abord, vous pouvez passer une chaîne littérale directement à impression():

>>>

>>> impression('Veuillez patienter pendant le chargement du programme ...')

Cela imprimera le message textuellement sur l'écran.

Littéraux de chaîne en Python peut être placé entre guillemets simples (') ou des guillemets doubles ("). Selon le guide de style officiel PEP 8, vous devez simplement en choisir un et continuer à l'utiliser de manière cohérente. Il n'y a pas de différence, sauf si vous devez en imbriquer un dans un autre.

Par exemple, vous ne pouvez pas utiliser de guillemets doubles pour le littéral et inclure également des guillemets doubles à l'intérieur de celui-ci, car cela est ambigu pour l'interpréteur Python:

"Mon livre favori est "Python Des trucs""  # Mal!

Ce que vous voulez faire est de mettre le texte, qui contient des guillemets doubles, entre guillemets simples:

"Mon livre préféré est" Python Tricks ""

La même astuce fonctionnerait dans l'autre sens:

"Mon livre préféré est" Python Tricks ""

Vous pouvez également utiliser les séquences de caractères d'échappement mentionnées précédemment, pour que Python traite ces guillemets internes littéralement comme faisant partie de la chaîne littérale:

"Mon livre favori est  "Astuces Python ""

S'échapper est bien et dandy, mais cela peut parfois gêner. Plus précisément, lorsque vous avez besoin que votre chaîne contienne relativement de nombreux caractères de barre oblique inverse sous forme littérale.

Un exemple classique est un chemin de fichier sous Windows:

'C:  Utilisateurs  jdoe'    # Mal!
«C:\Utilisateurs\jdoe '

Remarquez comment chaque caractère de barre oblique inverse doit être échappé avec une autre barre oblique inverse.

Ceci est encore plus important avec les expressions régulières, qui se compliquent rapidement en raison de l'utilisation intensive de caractères spéciaux:

'^\w:\\(? :(? :( ?:[^[^[^[^\\]+)? | (?:[^[^[^[^\\]+)\\[^[^[^[^\\]+) *) $ '

Heureusement, vous pouvez désactiver complètement l'échappement des caractères à l'aide de littéraux de chaîne brute. Ajoutez simplement un r ou R avant la citation d'ouverture, et maintenant vous vous retrouvez avec ceci:

r'C:  Utilisateurs  jdoe'
r'^  w:\(? :(? :( ?:[^[^[^[^\]+)? | (?:[^[^[^[^\]+)\[^[^[^[^\]+) *) $ '

C’est beaucoup mieux, non?

Il existe quelques préfixes supplémentaires qui donnent une signification particulière aux littéraux de chaîne en Python, mais vous ne les aborderez pas ici.

Enfin, vous pouvez définir des littéraux de chaîne multilignes en les plaçant entre '' ' ou "" ", qui sont souvent utilisés comme docstrings.

Voici un exemple:

"" "
Ceci est un exemple
d'une chaîne multiligne
en Python.
"" "

Pour éviter une nouvelle ligne initiale, placez simplement le texte juste après l'ouverture "" ":

"""Ceci est un exemple
d'une chaîne multiligne
en Python.
"" "

Vous pouvez également utiliser une barre oblique inverse pour vous débarrasser de la nouvelle ligne:

"" "
Ceci est un exemple
d'une chaîne multiligne
en Python.
"" " 

Pour supprimer l'indentation d'une chaîne multiligne, vous pouvez tirer parti de la fonction intégrée habillage de texte module:

>>>

>>> importer habillage de texte
>>> paragraphe = '' '
...                 Ceci est un exemple
...                 d'une chaîne multiligne
...                 en Python.
...                 '' '
...
>>> impression(paragraphe)

                Ceci est un exemple
                d'une chaîne multiligne
                en Python.

>>> impression(habillage de texte.dévoué(paragraphe).déshabiller())
Ceci est un exemple
d'une chaîne multiligne
en Python.

Cela prendra en charge la suppression de l'indentation des paragraphes pour vous. Il existe également quelques autres fonctions utiles dans habillage de texte pour l'alignement de texte que vous trouverez dans un traitement de texte.

Deuxièmement, vous pouvez extraire ce message dans sa propre variable avec un nom significatif pour améliorer la lisibilité et promouvoir la réutilisation du code:

>>>

>>> un message = 'Veuillez patienter pendant le chargement du programme ...'
>>> impression(un message)

Enfin, vous pouvez passer une expression, comme la concaténation de chaînes, à évaluer avant d'imprimer le résultat:

>>>

>>> importer os
>>> impression('Bonjour, ' + os.getlogin() + '! Comment allez-vous?')
Bonjour jdoe! Comment vas-tu?

En fait, il existe une douzaine de façons de formater des messages en Python. Je vous encourage vivement à jeter un coup d'œil aux f-strings, introduites dans Python 3.6, car elles offrent la syntaxe la plus concise de toutes:

>>>

>>> importer os
>>> impression(F'Bonjour, os.getlogin()! Comment allez-vous?')

De plus, les chaînes f vous éviteront de faire une erreur courante, qui est d'oublier de taper des opérandes concaténés. Python est un langage fortement typé, ce qui signifie qu'il ne vous permettra pas de faire cela:

>>>

>>> 'Mon âge est ' + 42
Traceback (dernier appel le plus récent):
  Déposer "", ligne 1, dans 
    'Mon âge est ' + 42
Erreur-type: ne peut concaténer que str (pas "int") en str

C’est faux, car l’ajout de nombres à des chaînes n’a pas de sens. Vous devez d'abord convertir explicitement le nombre en chaîne, afin de les joindre ensemble:

>>>

>>> 'Mon âge est ' + str(42)
'Mon âge est de 42 ans'

À moins que vous ne gériez vous-même de telles erreurs, l'interpréteur Python vous informera d'un problème en affichant une trace.

Comme pour toute fonction, peu importe que vous transmettiez un littéral, une variable ou une expression. Contrairement à de nombreuses autres fonctions, cependant, impression() acceptera n'importe quoi quel que soit son type.

Jusqu'à présent, vous n'avez regardé que la chaîne, mais qu'en est-il des autres types de données? Essayons des littéraux de différents types intégrés et voyons ce qui en ressort:

>>>

>>> impression(42)                            # 
42
>>> impression(3.14)                          # 
3.14
>>> impression(1 + 2j)                        # 
(1 + 2j)
>>> impression(Vrai)                          # 
Vrai
>>> impression([[[[1, 2, 3])                     # 
[1, 2, 3]
>>> impression((1, 2, 3))                     # 
(1, 2, 3)
>>> impression('rouge', 'vert', 'bleu')      # 
'rouge', 'vert', 'bleu'
>>> impression('Nom': «Alice», 'âge': 42)  # 
'nom': 'Alice', 'âge': 42
>>> impression('Bonjour')                       # 
Bonjour

Attention aux Aucun constante, cependant. Bien qu'il soit utilisé pour indiquer une absence de valeur, il apparaîtra comme 'Aucun' plutôt qu'une chaîne vide:

Comment ça marche impression() savoir comment travailler avec tous ces différents types? Eh bien, la réponse courte est que non. Il appelle implicitement str () dans les coulisses pour taper cast n'importe quel objet dans une chaîne. Ensuite, il traite les chaînes de manière uniforme.

Plus loin dans ce didacticiel, vous apprendrez à utiliser ce mécanisme pour imprimer des types de données personnalisés tels que vos classes.

D'accord, vous pouvez maintenant appeler impression() avec un seul argument ou sans aucun argument. Vous savez comment imprimer des messages fixes ou formatés sur l'écran. La prochaine sous-section développera un peu la mise en forme des messages.

Pour obtenir le même résultat dans la génération de langue précédente, vous souhaitez normalement supprimer les parenthèses entourant le texte:

# Python 2
impression
impression 'S'il vous plaît, attendez...'
impression 'Bonjour, % s! Comment allez-vous?' % os.getlogin()
impression 'Bonjour, % s. Votre âge est %ré». % (Nom, âge)

C'est parce que impression n’était pas une fonction à l’époque, comme vous le verrez dans la section suivante. Notez cependant que dans certains cas, les parenthèses en Python sont redondantes. Cela ne ferait pas de mal de les inclure car ils seraient simplement ignorés. Cela signifie-t-il que vous devriez utiliser le impression déclaration comme s'il s'agissait d'une fonction? Absolument pas!

Par exemple, les parenthèses entourant une seule expression ou un littéral sont facultatives. Les deux instructions produisent le même résultat dans Python 2:

>>>

>>> # Python 2
>>> impression 'S'il vous plaît, attendez...'
S'il vous plaît, attendez...
>>> impression('S'il vous plaît, attendez...')
S'il vous plaît, attendez...

Les crochets ronds font en fait partie de l'expression plutôt que du impression déclaration. Si votre expression ne contient qu'un seul élément, c'est comme si vous n'aviez pas du tout inclus les crochets.

D'autre part, mettre des parenthèses autour de plusieurs éléments forme un tuple:

>>>

>>> # Python 2
>>> impression 'Mon nom est', 'John'
Mon nom est John
>>> impression('Mon nom est', 'John')
('Mon nom est John')

C'est une source connue de confusion. En fait, vous obtiendrez également un tuple en ajoutant une virgule à la fin du seul élément entouré de parenthèses:

>>>

>>> # Python 2
>>> impression('S'il vous plaît, attendez...')
S'il vous plaît, attendez...
>>> impression('S'il vous plaît, attendez...',)  # Remarquez la virgule
('S'il vous plaît, attendez...',)

En fin de compte, vous ne devriez pas appeler impression avec des crochets en Python 2. Bien que, pour être tout à fait précis, vous pouvez contourner ce problème à l'aide d'un __avenir__ import, dont vous en apprendrez plus dans la section correspondante.

Séparer plusieurs arguments

Vous avez vu impression() appelé sans aucun argument pour produire une ligne vide, puis appelé avec un seul argument pour afficher un message fixe ou formaté.

Cependant, il s'avère que cette fonction peut accepter n'importe quel nombre de arguments de position, y compris zéro, un ou plusieurs arguments. C'est très pratique dans un cas courant de mise en forme de message, où vous souhaitez joindre quelques éléments ensemble.

Les arguments peuvent être passés à une fonction de plusieurs manières. Une façon consiste à nommer explicitement les arguments lorsque vous appelez la fonction, comme ceci:

>>>

>>> def div(une, b):
...     revenir une / b
...
>>> div(une=3, b=4)
0,75

Étant donné que les arguments peuvent être identifiés de manière unique par leur nom, leur ordre n’a pas d’importance. Les échanger donnera toujours le même résultat:

>>>

>>> div(b=4, une=3)
0,75

Inversement, les arguments passés sans nom sont identifiés par leur position. C'est pourquoi arguments de position doivent suivre strictement l'ordre imposé par la signature de la fonction:

>>>

>>> div(3, 4)
0,75
>>> div(4, 3)
1,3333333333333333

impression() permet un nombre arbitraire d'arguments positionnels grâce au * args paramètre.

Jetons un œil à cet exemple:

>>>

>>> importer os
>>> impression('Mon nom est', os.getlogin(), 'et je suis', 42)
Je m'appelle jdoe et j'ai 42 ans

impression() a concaténé les quatre arguments qui lui ont été transmis, et il a inséré un seul espace entre eux afin que vous ne vous retrouviez pas avec un message écrasé comme `` Je m'appelle Jdo et je suis 42 ''.

Notez qu'il a également pris soin de la conversion de type appropriée en appelant implicitement str () sur chaque argument avant de les réunir. Si vous vous souvenez de la sous-section précédente, une concaténation naïve peut facilement entraîner une erreur en raison de types incompatibles:

>>>

>>> impression('Mon âge est: ' + 42)
Traceback (dernier appel le plus récent):
  Déposer "", ligne 1, dans 
    impression('Mon âge est: ' + 42)
Erreur-type: ne peut concaténer que str (pas "int") en str

En plus d'accepter un nombre variable d'arguments positionnels, impression() définit quatre nommés ou arguments de mots clés, qui sont facultatifs car ils ont tous des valeurs par défaut. Vous pouvez consulter leur brève documentation en appelant aide (impression) de l'interprète interactif.

Concentrons-nous sur SEP juste pour l'instant. Ça signifie séparateur et se voit attribuer un seul espace ('') par défaut. Il détermine la valeur avec laquelle joindre des éléments.

Il doit s'agir d'une chaîne ou Aucun, mais ce dernier a le même effet que l'espace par défaut:

>>>

>>> impression('Bonjour', 'monde', SEP=Aucun)
Bonjour le monde
>>> impression('Bonjour', 'monde', SEP='')
Bonjour le monde
>>> impression('Bonjour', 'monde')
Bonjour le monde

Si vous souhaitez supprimer complètement le séparateur, vous devez passer une chaîne vide ('') au lieu:

>>>

>>> impression('Bonjour', 'monde', SEP='')
Bonjour le monde

Vous voudrez peut-être impression() pour joindre ses arguments sous forme de lignes séparées. Dans ce cas, passez simplement le caractère de nouvelle ligne échappé décrit précédemment:

>>>

>>> impression('Bonjour', 'monde', SEP=' n')
Bonjour
monde

Un exemple plus utile de la SEP paramètre imprimerait quelque chose comme les chemins de fichiers:

>>>

>>> impression('domicile', 'utilisateur', 'documents', SEP='/')
accueil / utilisateur / documents

N'oubliez pas que le séparateur se situe entre les éléments, pas autour d'eux, vous devez donc en tenir compte d'une manière ou d'une autre:

>>>

>>> impression('/domicile', 'utilisateur', 'documents', SEP='/')
/ home / utilisateur / documents
>>> impression('', 'domicile', 'utilisateur', 'documents', SEP='/')
/ home / utilisateur / documents

Plus précisément, vous pouvez insérer une barre oblique (/) dans le premier argument de position, ou utilisez une chaîne vide comme premier argument pour appliquer la barre oblique.

Un autre exemple intéressant pourrait être l'exportation de données au format CSV (valeurs séparées par des virgules):

>>>

>>> impression(1, 'Astuces Python', «Dan Bader», SEP=',')
1, astuces Python, Dan Bader

Cela ne gérerait pas correctement les cas extrêmes tels que les virgules d'échappement, mais pour des cas d'utilisation simples, cela devrait le faire. La ligne ci-dessus apparaîtra dans la fenêtre de votre terminal. Pour l'enregistrer dans un fichier, vous devez rediriger la sortie. Plus loin dans cette section, vous verrez comment utiliser impression() pour écrire du texte dans des fichiers directement à partir de Python.

Finalement, le SEP Le paramètre n’est pas limité à un seul caractère. Vous pouvez joindre des éléments avec des chaînes de n'importe quelle longueur:

>>>

>>> impression('nœud', 'enfant', 'enfant', SEP='->')
nœud -> enfant -> enfant

Dans les sous-sections à venir, vous découvrirez les arguments de mots clés restants de la impression() fonction.

Pour imprimer plusieurs éléments dans Python 2, vous devez supprimer les parenthèses autour d'eux, comme avant:

>>>

>>> # Python 2
>>> importer os
>>> impression 'Mon nom est', os.getlogin(), 'et je suis', 42
Je m'appelle jdoe et j'ai 42 ans

Si vous les avez conservés, en revanche, vous passerez un seul élément de tuple au impression déclaration:

>>>

>>> # Python 2
>>> importer os
>>> impression('Mon nom est', os.getlogin(), 'et je suis', 42)
('Je ​​m'appelle', 'jdoe', 'et je suis', 42)

De plus, il n'y a aucun moyen de modifier le séparateur par défaut des éléments joints dans Python 2, donc une solution de contournement consiste à utiliser l'interpolation de chaîne comme ceci:

>>>

>>> # Python 2
>>> importer os
>>> impression 'Mon nom est % s    et je suis %ré' % (os.getlogin(), 42)
Je m'appelle jdoe et j'ai 42 ans

C'était la manière par défaut de formater les chaînes jusqu'à ce que .format() méthode a été rétroportée à partir de Python 3.

Prévention des sauts de ligne

Parfois, vous ne souhaitez pas terminer votre message par une nouvelle ligne de fin afin que les appels suivants à impression() continuera sur la même ligne. Les exemples classiques incluent la mise à jour de la progression d'une opération de longue durée ou l'invitation à saisir l'utilisateur. Dans ce dernier cas, vous voulez que l'utilisateur tape la réponse sur la même ligne:

Es-tu sûr de vouloir faire ça? [y/n] y

De nombreux langages de programmation exposent des fonctions similaires à impression() via leurs bibliothèques standard, mais ils vous permettent de décider d'ajouter ou non une nouvelle ligne. Par exemple, en Java et C #, vous avez deux fonctions distinctes, tandis que d'autres langages vous obligent à ajouter explicitement n à la fin d'une chaîne littérale.

Voici quelques exemples de syntaxe dans ces langages:

Langue Exemple
Perl imprimer "bonjour le monde n"
C printf ("bonjour le monde n");
C ++ std :: cout << "bonjour le monde" << std :: endl;

En revanche, Python impression() la fonction ajoute toujours n sans demander, car c'est ce que vous voulez dans la plupart des cas. Pour le désactiver, vous pouvez profiter d'un autre argument de mot-clé, finir, qui dicte par quoi terminer la ligne.

En termes de sémantique, le finir paramètre est presque identique au SEP celui que vous avez vu plus tôt:

  • Ce doit être une chaîne ou Aucun.
  • Cela peut être arbitrairement long.
  • Il a une valeur par défaut de ' n'.
  • Si égal à Aucun, elle aura le même effet que la valeur par défaut.
  • Si égal à une chaîne vide (''), il supprimera la nouvelle ligne.

Vous comprenez maintenant ce qui se passe sous le capot lorsque vous appelez impression() sans arguments. Étant donné que vous ne fournissez aucun argument de position à la fonction, il n'y a rien à joindre et le séparateur par défaut n'est donc pas du tout utilisé. Cependant, la valeur par défaut de finir s'applique toujours, et une ligne vide apparaît.

Pour désactiver la nouvelle ligne, vous devez spécifier une chaîne vide à travers le finir argument de mot-clé:

impression('Vérification de l'intégrité des fichiers ...', finir='')
# (...)
impression('d'accord')

Même si ce sont deux séparés impression() les appels, qui peuvent s’exécuter très longtemps, vous ne verrez finalement qu’une seule ligne. Tout d'abord, cela ressemblera à ceci:

Vérification de l'intégrité des fichiers ...

Cependant, après le deuxième appel à impression(), la même ligne apparaîtra à l'écran comme:

Vérification de l'intégrité du fichier ... ok

Comme avec SEP, vous pouvez utiliser finir pour joindre des morceaux individuels dans une grosse goutte de texte avec un séparateur personnalisé. Au lieu de joindre plusieurs arguments, cependant, il ajoutera le texte de chaque appel de fonction sur la même ligne:

impression('La première phrase', finir=». ')
impression('La deuxième phrase', finir=». ')
impression('La dernière phrase.')

Ces trois instructions produiront une seule ligne de texte:

La première phrase. La deuxième phrase. La dernière phrase.

Vous pouvez mélanger les deux arguments de mot-clé:

impression('Mercure', 'Vénus', 'La terre', SEP=',', finir=',')
impression('Mars', 'Jupiter', 'Saturne', SEP=',', finir=',')
impression('Uranus', 'Neptune', 'Pluton', SEP=',')

Non seulement vous obtenez une seule ligne de texte, mais tous les éléments sont séparés par une virgule:

Mercure, Vénus, Terre, Mars, Jupiter, Saturne, Uranus, Neptune, Pluton

Rien ne vous empêche d'utiliser le caractère de nouvelle ligne avec un peu de remplissage supplémentaire autour de celui-ci:

impression(«L'impression en bref», finir=' n    * ')
impression("Appel de Print", finir=' n    * ')
impression(«Séparer plusieurs arguments», finir=' n    * ')
impression('Prévention des sauts de ligne')

Il imprimerait le texte suivant:

L'impression en bref
 * Appel de Print
 * Séparer plusieurs arguments
 * Prévenir les sauts de ligne

Comme vous pouvez le voir, le finir L'argument mot-clé acceptera des chaînes arbitraires.

Vous vous familiarisez avec l’impression en Python, mais il reste encore beaucoup d’informations utiles à venir. Dans la sous-section à venir, vous apprendrez comment intercepter et rediriger le impression() sortie de la fonction.

Pour empêcher un saut de ligne dans Python 2, vous devez ajouter une virgule de fin à l'expression:

Cependant, ce n’est pas idéal, car cela ajoute également un espace indésirable, ce qui se traduirait par fin = '' au lieu de fin = '' en Python 3. Vous pouvez tester cela avec l'extrait de code suivant:

impression 'AVANT QUE'
impression 'Bonjour',
impression 'APRÈS'

Remarquez qu'il y a un espace entre les mots Bonjour et APRÈS:

Pour obtenir le résultat attendu, vous devez utiliser l’une des astuces expliquées plus loin, à savoir l’importation du impression() fonction de __avenir__ ou retomber au sys module:

importer sys
impression 'AVANT QUE'
sys.stdout.écrivez('Bonjour')
impression 'APRÈS' 

Cela imprimera la sortie correcte sans espace supplémentaire:

Lors de l'utilisation du sys module vous donne le contrôle sur ce qui est imprimé sur la sortie standard, le code devient un peu plus encombré.

Impression dans un fichier

Croyez-le ou non, impression() ne sait pas comment transformer les messages en texte sur votre écran, et franchement, il n’en a pas besoin. C'est un travail pour les couches de code de niveau inférieur, qui comprennent les octets et savent comment les déplacer.

impression() est une abstraction sur ces couches, fournissant une interface pratique qui délègue simplement l'impression réelle à un flux ou objet semblable à un fichier. Un flux peut être n'importe quel fichier sur votre disque, une socket réseau ou peut-être un tampon en mémoire.

En plus de cela, il existe trois flux standard fournis par le système d'exploitation:

  1. stdin: entrée standard
  2. stdout: sortie standard
  3. stderr: erreur standard

Sortie standard est ce que vous voyez dans le terminal lorsque vous exécutez divers programmes de ligne de commande, y compris vos propres scripts Python:

$ chat bonjour.py 
print ('Cela apparaîtra sur stdout')
$ python bonjour.py
Cela apparaîtra sur stdout

Sauf indication contraire, impression() sera par défaut d'écrire sur la sortie standard. Cependant, vous pouvez demander à votre système d'exploitation de changer temporairement stdout pour un flux de fichiers, de sorte que toute sortie aboutisse dans ce fichier plutôt que dans l'écran:

$ python hello.py> fichier.txt
$ cat file.txt
Cela apparaîtra sur stdout

C'est ce qu'on appelle la redirection de flux.

L'erreur standard est similaire à stdout en ce qu 'il apparaît également à l'écran. Néanmoins, il s’agit d’un flux distinct, dont le but est de consigner les messages d’erreur à des fins de diagnostic. En redirigeant l'un d'entre eux ou les deux, vous pouvez garder les choses propres.

Certains programmes utilisent des couleurs différentes pour distinguer les messages imprimés sur stdout et stderr:

La sortie d'un programme exécuté dans PyCharm
Exécuter la fenêtre de l'outil dans PyCharm

Alors que les deux stdout et stderr sont en écriture seule, stdin est en lecture seule. Vous pouvez considérer l'entrée standard comme votre clavier, mais tout comme les deux autres, vous pouvez échanger stdin pour un fichier à partir duquel lire les données.

En Python, vous pouvez accéder à tous les flux standard via le sys module:

>>>

>>> importer sys
>>> sys.stdin
<_io.TextIOWrapper name = ''mode =' r 'encoding =' UTF-8 '>
>>> sys.stdin.fileno()
0
>>> sys.stdout
<_io.TextIOWrapper name = ''mode =' w 'encoding =' UTF-8 '>
>>> sys.stdout.fileno()
1
>>> sys.stderr
<_io.TextIOWrapper name = ''mode =' w 'encoding =' UTF-8 '>
>>> sys.stderr.fileno()
2

Comme vous pouvez le voir, ces valeurs prédéfinies ressemblent à des objets de type fichier avec mode et codage attributs ainsi que .lire() et .écrivez() méthodes parmi tant d’autres.

Par défaut, impression() est lié à sys.stdout à travers son déposer argument, mais vous pouvez changer cela. Utilisez cet argument de mot-clé pour indiquer un fichier ouvert en mode écriture ou ajout, afin que les messages y accèdent directement:

avec ouvert("file.txt", mode=«w») comme objet_fichier:
    impression('Bonjour le monde', déposer=objet_fichier)

Cela rendra votre code immunisé contre la redirection de flux au niveau du système d'exploitation, ce qui pourrait être souhaité ou non.

Pour plus d'informations sur l'utilisation des fichiers en Python, vous pouvez consulter Lire et écrire des fichiers en Python (Guide).

Notez que impression() n'a aucun contrôle sur le codage des caractères. Il est de la responsabilité du flux d’encoder correctement les chaînes Unicode reçues en octets. Dans la plupart des cas, vous ne définissez pas l'encodage vous-même, car l'UTF-8 par défaut est ce que vous voulez. Si vous en avez vraiment besoin, peut-être pour les systèmes hérités, vous pouvez utiliser le codage argument de ouvert():

avec ouvert("file.txt", mode=«w», codage=«iso-8859-1») comme objet_fichier:
    impression(«über café naïf», déposer=file_object)

Instead of a real file existing somewhere in your file system, you can provide a fake one, which would reside in your computer’s memory. You’ll use this technique later for mocking print() in unit tests:

>>>

>>> importer io
>>> fake_file = io.StringIO()
>>> impression('hello world', déposer=fake_file)
>>> fake_file.getvalue()
'hello worldn'

If you got to this point, then you’re left with only one keyword argument in print(), which you’ll see in the next subsection. It’s probably the least used of them all. Nevertheless, there are times when it’s absolutely necessary.

There’s a special syntax in Python 2 for replacing the default sys.stdout with a custom file in the impression statement:

avec ouvert('file.txt', mode='w') comme file_object:
    impression >> file_object, 'hello world'

Because strings and bytes are represented with the same str type in Python 2, the impression statement can handle binary data just fine:

avec ouvert('file.dat', mode='wb') comme file_object:
    impression >> file_object, 'x41x0a'

Although, there’s a problem with character encoding. Le open() function in Python 2 lacks the encoding parameter, which would often result in the dreadful UnicodeEncodeError:

>>>

>>> avec ouvert('file.txt', mode='w') comme file_object:
...     unicode_text = u'xfcber naxefve cafxe9'
...     impression >> file_object, unicode_text
... 
Traceback (most recent call last):
  Déposer "", line 3, dans 
UnicodeEncodeError: 'ascii' codec can't encode character u'xfc'...

Notice how non-Latin characters must be escaped in both Unicode and string literals to avoid a syntax error. Take a look at this example:

unicode_literal = u'xfcber naxefve cafxe9'
string_literal = 'xc3xbcber naxc3xafve cafxc3xa9'

Alternatively, you could specify source code encoding according to PEP 263 at the top of the file, but that wasn’t the best practice due to portability issues:

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

unescaped_unicode_literal = u'über naïve café'
unescaped_string_literal = 'über naïve café'

Your best bet is to encode the Unicode string just before printing it. You can do this manually:

avec ouvert('file.txt', mode='w') comme file_object:
    unicode_text = u'xfcber naxefve cafxe9'
    encoded_text = unicode_text.encode('utf-8')
    impression >> file_object, encoded_text

However, a more convenient option is to use the built-in codecs module:

importer codecs

avec codecs.ouvert('file.txt', 'w', encoding='utf-8') comme file_object:
    unicode_text = u'xfcber naxefve cafxe9'
    impression >> file_object, unicode_text

It’ll take care of making appropriate conversions when you need to read or write files.

Buffering print() Calls

In the previous subsection, you learned that print() delegates printing to a file-like object such as sys.stdout. Some streams, however, buffer certain I/O operations to enhance performance, which can get in the way. Let’s take a look at an example.

Imagine you were writing a countdown timer, which should append the remaining time to the same line every second:

Your first attempt may look something like this:

importer temps

num_seconds = 3
pour countdown dans renversé(range(num_seconds + 1)):
    si countdown > 0:
        impression(countdown, finir='...')
        temps.dormir(1)
    autre:
        impression('Go!')

As long as the countdown variable is greater than zero, the code keeps appending text without a trailing newline and then goes to sleep for one second. Finally, when the countdown is finished, it prints Aller! and terminates the line.

Unexpectedly, instead of counting down every second, the program idles wastefully for three seconds, and then suddenly prints the entire line at once:

Terminal with buffered output

That’s because the operating system buffers subsequent writes to the standard output in this case. You need to know that there are three kinds of streams with respect to buffering:

  1. Unbuffered
  2. Line-buffered
  3. Block-buffered

Unbuffered is self-explanatory, that is, no buffering is taking place, and all writes have immediate effect. UNE line-buffered stream waits before firing any I/O calls until a line break appears somewhere in the buffer, whereas a block-buffered one simply allows the buffer to fill up to a certain size regardless of its content. Standard output is both line-buffered et block-buffered, depending on which event comes first.

Buffering helps to reduce the number of expensive I/O calls. Think about sending messages over a high-latency network, for example. When you connect to a remote server to execute commands over the SSH protocol, each of your keystrokes may actually produce an individual data packet, which is orders of magnitude bigger than its payload. What an overhead! It would make sense to wait until at least a few characters are typed and then send them together. That’s where buffering steps in.

On the other hand, buffering can sometimes have undesired effects as you just saw with the countdown example. To fix it, you can simply tell print() to forcefully flush the stream without waiting for a newline character in the buffer using its affleurer flag:

impression(countdown, finir='...', affleurer=Vrai)

C'est tout. Your countdown should work as expected now, but don’t take my word for it. Go ahead and test it to see the difference.

Toutes nos félicitations! At this point, you’ve seen examples of calling print() that cover all of its parameters. You know their purpose and when to use them. Understanding the signature is only the beginning, however. In the upcoming sections, you’ll see why.

There isn’t an easy way to flush the stream in Python 2, because the impression statement doesn’t allow for it by itself. You need to get a handle of its lower-level layer, which is the standard output, and call it directly:

importer temps
importer sys

num_seconds = 3
pour countdown dans renversé(range(num_seconds + 1)):
    si countdown > 0:
        sys.stdout.écrivez('%s...' % countdown)
        sys.stdout.affleurer()
        temps.dormir(1)
    autre:
        impression 'Go!'

Alternatively, you could disable buffering of the standard streams either by providing the -u flag to the Python interpreter or by setting up the PYTHONUNBUFFERED environment variable:

$ python2 -u countdown.py
$ PYTHONUNBUFFERED=1 python2 countdown.py

Notez que print() was backported to Python 2 and made available through the __future__ module. Unfortunately, it doesn’t come with the affleurer parameter:

>>>

>>> de __future__ importer print_function
>>> aider(impression)
Help on built-in function print in module __builtin__:

print(...)
                print(value, ..., sep=' ', end='n', file=sys.stdout)

What you’re seeing here is a docstring de la print() fonction. You can display docstrings of various objects in Python using the built-in help() fonction.

Printing Custom Data Types

Up until now, you only dealt with built-in data types such as strings and numbers, but you’ll often want to print your own abstract data types. Let’s have a look at different ways of defining them.

For simple objects without any logic, whose purpose is to carry data, you’ll typically take advantage of namedtuple, which is available in the standard library. Named tuples have a neat textual representation out of the box:

>>>

>>> de collections importer namedtuple
>>> La personne = namedtuple('Person', 'name age')
>>> jdoe = La personne('John Doe', 42)
>>> impression(jdoe)
Person(name='John Doe', age=42)

That’s great as long as holding data is enough, but in order to add behaviors to the La personne type, you’ll eventually need to define a class. Take a look at this example:

classer La personne:
    def __init__(soi, Nom, âge):
        soi.Nom, soi.âge = Nom, âge

If you now create an instance of the La personne class and try to print it, you’ll get this bizarre output, which is quite different from the equivalent namedtuple:

>>>

>>> jdoe = La personne('John Doe', 42)
>>> impression(jdoe)
<__main__.Person object at 0x7fcac3fed1d0>

It’s the default representation of objects, which comprises their address in memory, the corresponding class name and a module in which they were defined. You’ll fix that in a bit, but just for the record, as a quick workaround you could combine namedtuple and a custom class through inheritance:

de collections importer namedtuple

classer La personne(namedtuple('Person', 'name age')):
    passe

Ton La personne class has just become a specialized kind of namedtuple with two attributes, which you can customize.

That’s better than a plain namedtuple, because not only do you get printing right for free, but you can also add custom methods and properties to the class. However, it solves one problem while introducing another. Remember that tuples, including named tuples, are immutable in Python, so they can’t change their values once created.

It’s true that designing immutable data types is desirable, but in many cases, you’ll want them to allow for change, so you’re back with regular classes again.

From earlier subsections, you already know that print() implicitly calls the built-in str() function to convert its positional arguments into strings. Indeed, calling str() manually against an instance of the regular La personne class yields the same result as printing it:

>>>

>>> jdoe = La personne('John Doe', 42)
>>> str(jdoe)
'<__main__.Person object at 0x7fcac3fed1d0>'

str(), in turn, looks for one of two magic methods within the class body, which you typically implement. If it doesn’t find one, then it falls back to the ugly default representation. Those magic methods are, in order of search:

  1. def __str__(self)
  2. def __repr__(self)

The first one is recommended to return a short, human-readable text, which includes information from the most relevant attributes. After all, you don’t want to expose sensitive data, such as user passwords, when printing objects.

However, the other one should provide complete information about an object, to allow for restoring its state from a string. Ideally, it should return valid Python code, so that you can pass it directly to eval():

>>>

>>> repr(jdoe)
"Person(name='John Doe', age=42)"
>>> taper(eval(repr(jdoe)))

Notice the use of another built-in function, repr(), which always tries to call .__repr__() in an object, but falls back to the default representation if it doesn’t find that method.

Python gives you a lot of freedom when it comes to defining your own data types if none of the built-in ones meet your needs. Some of them, such as named tuples and data classes, offer string representations that look good without requiring any work on your part. Still, for the most flexibility, you’ll have to define a class and override its magic methods described above.

The semantics of .__str__() et .__repr__() didn’t change since Python 2, but you must remember that strings were nothing more than glorified byte arrays back then. To convert your objects into proper Unicode, which was a separate data type, you’d have to provide yet another magic method: .__unicode__().

Here’s an example of the same Utilisateur class in Python 2:

classer Utilisateur(object):
    def __init__(soi, login, password):
        soi.login = login
        soi.password = password

    def __unicode__(soi):
        return soi.login

    def __str__(soi):
        return unicode(soi).encode('utf-8')

    def __repr__(soi):
        utilisateur = u"User('%s', '%s')" % (soi.login, soi.password)
        return utilisateur.encode('unicode_escape')

As you can see, this implementation delegates some work to avoid duplication by calling the built-in unicode() function on itself.

Tous les deux .__str__() et .__repr__() methods must return strings, so they encode Unicode characters into specific byte representations called jeux de caractères. UTF-8 is the most widespread and safest encoding, while unicode_escape is a special constant to express funky characters, such as é, as escape sequences in plain ASCII, such as xe9.

Le impression statement is looking for the magic .__str__() method in the class, so the chosen charset must correspond to the one used by the terminal. For example, default encoding in DOS and Windows is CP 852 rather than UTF-8, so running this can result in a UnicodeEncodeError or even garbled output:

>>>

>>> utilisateur = Utilisateur(u'u043du0438u043au0438u0442u0430', u's3cret')
>>> impression utilisateur
đŻđŞđ║đŞĐéđ░

However, if you ran the same code on a system with UTF-8 encoding, then you’d get the proper spelling of a popular Russian name:

>>>

>>> utilisateur = Utilisateur(u'u043du0438u043au0438u0442u0430', u's3cret')
>>> impression utilisateur
никита

It’s recommended to convert strings to Unicode as early as possible, for example, when you’re reading data from a file, and use it consistently everywhere in your code. At the same time, you should encode Unicode back to the chosen character set right before presenting it to the user.

It seems as if you have more control over string representation of objects in Python 2 because there’s no magic .__unicode__() method in Python 3 anymore. You may be asking yourself if it’s possible to convert an object to its byte string representation rather than a Unicode string in Python 3. It’s possible, with a special .__bytes__() method that does just that:

>>>

>>> classer Utilisateur(object):
...     def __init__(soi, login, password):
...         soi.login = login
...         soi.password = password
...     
...     def __bytes__(soi):  # Python 3
...         return soi.login.encode('utf-8')
...
>>> utilisateur = Utilisateur(u'u043du0438u043au0438u0442u0430', u's3cret')
>>> octets(utilisateur)
b'xd0xbdxd0xb8xd0xbaxd0xb8xd1x82xd0xb0'

Using the built-in bytes() function on an instance delegates the call to its __bytes__() method defined in the corresponding class.

Understanding Python print()

You know comment utiliser print() quite well at this point, but knowing Quel it is will allow you to use it even more effectively and consciously. After reading this section, you’ll understand how printing in Python has improved over the years.

Printing With Style

If you thought that printing was only about lighting pixels up on the screen, then technically you’d be right. However, there are ways to make it look cool. In this section, you’ll find out how to format complex data structures, add colors and other decorations, build interfaces, use animation, and even play sounds with text!

Pretty-Printing Nested Data Structures

Computer languages allow you to represent data as well as executable code in a structured way. Unlike Python, however, most languages give you a lot of freedom in using whitespace and formatting. This can be useful, for example in compression, but it sometimes leads to less readable code.

Pretty-printing is about making a piece of data or code look more appealing to the human eye so that it can be understood more easily. This is done by indenting certain lines, inserting newlines, reordering elements, and so forth.

Python comes with the pprint module in its standard library, which will help you in pretty-printing large data structures that don’t fit on a single line. Because it prints in a more human-friendly way, many popular REPL tools, including JupyterLab and IPython, use it by default in place of the regular print() fonction.

If you don’t care about not having access to the original print() function, then you can replace it with pprint() in your code using import renaming:

>>>

>>> de pprint importer pprint comme impression
>>> impression

Personally, I like to have both functions at my fingertips, so I’d rather use something like pp as a short alias:

de pprint importer pprint comme pp

At first glance, there’s hardly any difference between the two functions, and in some cases there’s virtually none:

>>>

>>> impression(42)
42
>>> pp(42)
42
>>> impression('hello')
Bonjour
>>> pp('hello')
'hello'  # Did you spot the difference?

That’s because pprint() appels repr() instead of the usual str() for type casting, so that you may evaluate its output as Python code if you want to. The differences become apparent as you start feeding it more complex data structures:

>>>

>>> Les données = 'powers': [[[[X**dix pour X dans range(dix)]
>>> pp(Les données)
'powers':[0[0[0[0
                                                1,
                                                1024,
                                                59049,
                                                1048576,
                                                9765625,
                                                60466176,
                                                282475249,
                                                1073741824,
                                                3486784401]

The function applies reasonable formatting to improve readability, but you can customize it even further with a couple of parameters. For example, you may limit a deeply nested hierarchy by showing an ellipsis below a given level:

>>>

>>> villes = 'USA': 'Texas': 'Dallas': [[[['Irving']
>>> pp(villes, profondeur=3)
'USA': 'Texas': 'Dallas': [...]

The ordinary print() also uses ellipses but for displaying recursive data structures, which form a cycle, to avoid stack overflow error:

>>>

>>> items = [[[[1, 2, 3]
>>> items.append(items)
>>> impression(items)
[123[123[123[123[...]]

Pourtant, pprint() is more explicit about it by including the unique identity of a self-referencing object:

>>>

>>> pp(items)
[123[123[123[123]
>>> identifiant(items)
140635757287688

The last element in the list is the same object as the entire list.

pprint() automatically sorts dictionary keys for you before printing, which allows for consistent comparison. When you’re comparing strings, you often don’t care about a particular order of serialized attributes. Anyways, it’s always best to compare actual dictionaries before serialization.

Dictionaries often represent JSON data, which is widely used on the Internet. To correctly serialize a dictionary into a valid JSON-formatted string, you can take advantage of the json module. It too has pretty-printing capabilities:

>>>

>>> importer json
>>> Les données = 'username': 'jdoe', 'password': 's3cret'
>>> ugly = json.dumps(Les données)
>>> pretty = json.dumps(Les données, indent=4, sort_keys=Vrai)
>>> impression(ugly)
"username": "jdoe", "password": "s3cret"
>>> impression(pretty)

                "password": "s3cret",
                "username": "jdoe"

Notice, however, that you need to handle printing yourself, because it’s not something you’d typically want to do. De même, le pprint module has an additional pformat() function that returns a string, in case you had to do something other than printing it.

Surprisingly, the signature of pprint() is nothing like the print() function’s one. You can’t even pass more than one positional argument, which shows how much it focuses on printing data structures.

Adding Colors With ANSI Escape Sequences

As personal computers got more sophisticated, they had better graphics and could display more colors. However, different vendors had their own idea about the API design for controlling it. That changed a few decades ago when people at the American National Standards Institute decided to unify it by defining ANSI escape codes.

Most of today’s terminal emulators support this standard to some degree. Until recently, the Windows operating system was a notable exception. Therefore, if you want the best portability, use the colorama library in Python. It translates ANSI codes to their appropriate counterparts in Windows while keeping them intact in other operating systems.

To check if your terminal understands a subset of the ANSI escape sequences, for example, related to colors, you can try using the following command:

My default terminal on Linux says it can display 256 distinct colors, while xterm gives me only 8. The command would return a negative number if colors were unsupported.

ANSI escape sequences are like a markup language for the terminal. In HTML you work with tags, such as ou , to change how elements look in the document. These tags are mixed with your content, but they’re not visible themselves. Similarly, escape codes won’t show up in the terminal as long as it recognizes them. Otherwise, they’ll appear in the literal form as if you were viewing the source of a website.

As its name implies, a sequence must begin with the non-printable Esc character, whose ASCII value is 27, sometimes denoted as 0x1b in hexadecimal or 033 in octal. You may use Python number literals to quickly verify it’s indeed the same number:

>>>

>>> 27 == 0x1b == 0o33
Vrai

Additionally, you can obtain it with the e escape sequence in the shell:

The most common ANSI escape sequences take the following form:

Élément Description Exemple
Esc non-printable escape character 33
[[[[ opening square bracket [[[[
numeric code one or more numbers separated with ; 0
character code uppercase or lowercase letter m

Le numeric code can be one or more numbers separated with a semicolon, while the character code is just one letter. Their specific meaning is defined by the ANSI standard. For example, to reset all formatting, you would type one of the following commands, which use the code zero and the letter m:

$ écho -e "e[0m"[0m"[0m"[0m"
$ écho -e "x1b[0m"[0m"[0m"[0m"
$ écho -e "33[0m"[0m"[0m"[0m"

At the other end of the spectrum, you have compound code values. To set foreground and background with RGB channels, given that your terminal supports 24-bit depth, you could provide multiple numbers:

$ écho -e "e[38;2;0;0;0me[48;2;255;255;255mBlackonwhitee[0m"[38;2;0;0;0me[48;2;255;255;255mBlackonwhitee[0m"[38;2;0;0;0me[48;2;255;255;255mBlackonwhitee[0m"[38;2;0;0;0me[48;2;255;255;255mBlackonwhitee[0m"

It’s not just text color that you can set with the ANSI escape codes. You can, for example, clear and scroll the terminal window, change its background, move the cursor around, make the text blink or decorate it with an underline.

In Python, you’d probably write a helper function to allow for wrapping arbitrary codes into a sequence:

>>>

>>> def esc(code):
...     return F'33[[[[codem'
...
>>> impression(esc('31;1;4') + 'really' + esc(0) + ' important')

This would make the word vraiment appear in red, bold, and underlined font:

Text formatted with ANSI escape codes

However, there are higher-level abstractions over ANSI escape codes, such as the mentioned colorama library, as well as tools for building user interfaces in the console.

Building Console User Interfaces

While playing with ANSI escape codes is undeniably a ton of fun, in the real world you’d rather have more abstract building blocks to put together a user interface. There are a few libraries that provide such a high level of control over the terminal, but curses seems to be the most popular choice.

Primarily, it allows you to think in terms of independent graphical widgets instead of a blob of text. Besides, you get a lot of freedom in expressing your inner artist, because it’s really like painting a blank canvas. The library hides the complexities of having to deal with different terminals. Other than that, it has great support for keyboard events, which might be useful for writing video games.

How about making a retro snake game? Let’s create a Python snake simulator:

The retro snake game built with curses library

First, you need to import the curses module. Since it modifies the state of a running terminal, it’s important to handle errors and gracefully restore the previous state. You can do this manually, but the library comes with a convenient wrapper for your main function:

importer curses

def principale(écran):
    passe

si __name__ == '__main__':
    curses.wrapper(principale)

Note, the function must accept a reference to the screen object, also known as stdscr, that you’ll use later for additional setup.

If you run this program now, you won’t see any effects, because it terminates immediately. However, you can add a small delay to have a sneak peek:

importer temps, curses

def principale(écran):
    temps.dormir(1)

si __name__ == '__main__':
    curses.wrapper(principale)

This time the screen went completely blank for a second, but the cursor was still blinking. To hide it, just call one of the configuration functions defined in the module:

importer temps, curses

def principale(écran):
    curses.curs_set(0)  # Hide the cursor
    temps.dormir(1)

si __name__ == '__main__':
    curses.wrapper(principale)

Let’s define the snake as a list of points in screen coordinates:

snake = [([([([(0, je) pour je dans renversé(range(20))]

The head of the snake is always the first element in the list, whereas the tail is the last one. The initial shape of the snake is horizontal, starting from the top-left corner of the screen and facing to the right. While its y-coordinate stays at zero, its x-coordinate decreases from head to tail.

To draw the snake, you’ll start with the head and then follow with the remaining segments. Each segment carries (y, x) coordinates, so you can unpack them:

# Draw the snake
écran.addstr(*snake[[[[0], '@')
pour segment dans snake[[[[1:]:
    écran.addstr(*segment, '*')

Again, if you run this code now, it won’t display anything, because you must explicitly refresh the screen afterward:

importer temps, curses

def principale(écran):
    curses.curs_set(0)  # Hide the cursor

    snake = [([([([(0, je) pour je dans renversé(range(20))]

    # Draw the snake
    écran.addstr(*snake[[[[0], '@')
    pour segment dans snake[[[[1:]:
        écran.addstr(*segment, '*')

    écran.rafraîchir()
    temps.dormir(1)

si __name__ == '__main__':
    curses.wrapper(principale)

You want to move the snake in one of four directions, which can be defined as vectors. Eventually, the direction will change in response to an arrow keystroke, so you may hook it up to the library’s key codes:

directions = 
    curses.KEY_UP: (-1, 0),
    curses.KEY_DOWN: (1, 0),
    curses.KEY_LEFT: (0, -1),
    curses.KEY_RIGHT: (0, 1),


direction = directions[[[[curses.KEY_RIGHT]

How does a snake move? It turns out that only its head really moves to a new location, while all other segments shift towards it. In each step, almost all segments remain the same, except for the head and the tail. Assuming the snake isn’t growing, you can remove the tail and insert a new head at the beginning of the list:

# Move the snake
snake.pop()
snake.insérer(0, tuple(carte(sum, zip(snake[[[[0], direction))))

To get the new coordinates of the head, you need to add the direction vector to it. However, adding tuples in Python results in a bigger tuple instead of the algebraic sum of the corresponding vector components. One way to fix this is by using the built-in zip(), sum(), et map() functions.

The direction will change on a keystroke, so you need to call .getch() to obtain the pressed key code. However, if the pressed key doesn’t correspond to the arrow keys defined earlier as dictionary keys, the direction won’t change:

# Change direction on arrow keystroke
direction = directions.get(écran.getch(), direction)

By default, however, .getch() is a blocking call that would prevent the snake from moving unless there was a keystroke. Therefore, you need to make the call non-blocking by adding yet another configuration:

def principale(écran):
    curses.curs_set(0)    # Hide the cursor
    écran.nodelay(Vrai)  # Don't block I/O calls

You’re almost done, but there’s just one last thing left. If you now loop this code, the snake will appear to be growing instead of moving. That’s because you have to erase the screen explicitly before each iteration.

Finally, this is all you need to play the snake game in Python:

importer temps, curses

def principale(écran):
    curses.curs_set(0)    # Hide the cursor
    écran.nodelay(Vrai)  # Don't block I/O calls

    directions = 
        curses.KEY_UP: (-1, 0),
        curses.KEY_DOWN: (1, 0),
        curses.KEY_LEFT: (0, -1),
        curses.KEY_RIGHT: (0, 1),
    

    direction = directions[[[[curses.KEY_RIGHT]
    snake = [([([([(0, je) pour je dans renversé(range(20))]

    tandis que Vrai:
        écran.erase()

        # Draw the snake
        écran.addstr(*snake[[[[0], '@')
        pour segment dans snake[[[[1:]:
            écran.addstr(*segment, '*')

        # Move the snake
        snake.pop()
        snake.insérer(0, tuple(carte(sum, zip(snake[[[[0], direction))))

        # Change direction on arrow keystroke
        direction = directions.get(écran.getch(), direction)

        écran.rafraîchir()
        temps.dormir(0.1)

si __name__ == '__main__':
    curses.wrapper(principale)

This is merely scratching the surface of the possibilities that the curses module opens up. You may use it for game development like this or more business-oriented applications.

Living It Up With Cool Animations

Not only can animations make the user interface more appealing to the eye, but they also improve the overall user experience. When you provide early feedback to the user, for example, they’ll know if your program’s still working or if it’s time to kill it.

To animate text in the terminal, you have to be able to freely move the cursor around. You can do this with one of the tools mentioned previously, that is ANSI escape codes or the curses bibliothèque. However, I’d like to show you an even simpler way.

If the animation can be constrained to a single line of text, then you might be interested in two special escape character sequences:

  • Carriage return: r
  • Backspace: b

The first one moves the cursor to the beginning of the line, whereas the second one moves it only one character to the left. They both work in a non-destructive way without overwriting text that’s already been written.

Let’s take a look at a few examples.

You’ll often want to display some kind of a spinning wheel to indicate a work in progress without knowing exactly how much time’s left to finish:

Indefinite animation in the terminal

Many command line tools use this trick while downloading data over the network. You can make a really simple stop motion animation from a sequence of characters that will cycle in a round-robin fashion:

de itertools importer cycle
de temps importer dormir

pour Cadre dans cycle(r'-|/-|/'):
    impression('r', Cadre, sep='', finir='', affleurer=Vrai)
    dormir(0.2)

The loop gets the next character to print, then moves the cursor to the beginning of the line, and overwrites whatever there was before without adding a newline. You don’t want extra space between positional arguments, so separator argument must be blank. Also, notice the use of Python’s raw strings due to backslash characters present in the literal.

When you know the remaining time or task completion percentage, then you’re able to show an animated progress bar:

Progress bar animation in the terminal

First, you need to calculate how many hashtags to display and how many blank spaces to insert. Next, you erase the line and build the bar from scratch:

de temps importer dormir

def progress(pour cent=0, largeur=30):
    left = largeur * pour cent // 100
    droit = largeur - left
    impression('r['['['[', '#' * left, ' ' * droit, ']',
          F' pour cent:.0f%',
          sep='', finir='', affleurer=Vrai)

pour je dans range(101):
    progress(je)
    dormir(0.1)

As before, each request for update repaints the entire line.

Making Sounds With print()

If you’re old enough to remember computers with a PC speaker, then you must also remember their distinctive beep sound, often used to indicate hardware problems. They could barely make any more noises than that, yet video games seemed so much better with it.

Today you can still take advantage of this small loudspeaker, but chances are your laptop didn’t come with one. In such a case, you can enable terminal bell emulation in your shell, so that a system warning sound is played instead.

Go ahead and type this command to see if your terminal can play a sound:

This would normally print text, but the -e flag enables the interpretation of backslash escapes. As you can see, there’s a dedicated escape sequence a, which stands for “alert”, that outputs a special bell character. Some terminals make a sound whenever they see it.

Similarly, you can print this character in Python. Perhaps in a loop to form some kind of melody. While it’s only a single note, you can still vary the length of pauses between consecutive instances. That seems like a perfect toy for Morse code playback!

The rules are the following:

  • Letters are encoded with a sequence of dot (·) and dash (–) symbols.
  • UNE dot is one unit of time.
  • UNE dash is three units of time.
  • Individuel symboles in a letter are spaced one unit of time apart.
  • Symbols of two adjacent letters are spaced three units of time apart.
  • Symbols of two adjacent mots are spaced seven units of time apart.

According to those rules, you could be “printing” an SOS signal indefinitely in the following way:

tandis que Vrai:
    dot()
    symbol_space()
    dot()
    symbol_space()
    dot()
    letter_space()
    dash()
    symbol_space()
    dash()
    symbol_space()
    dash()
    letter_space()
    dot()
    symbol_space()
    dot()
    symbol_space()
    dot()
    word_space()

In Python, you can implement it in merely ten lines of code:

de temps importer dormir

la vitesse = 0.1

def signal(duration, symbole):
    dormir(duration)
    impression(symbole, finir='', affleurer=Vrai)

dot = lambda: signal(la vitesse, a')
dash = lambda: signal(3*la vitesse, '−a')
symbol_space = lambda: signal(la vitesse, '')
letter_space = lambda: signal(3*la vitesse, '')
word_space = lambda: signal(7*la vitesse, ' ')

Maybe you could even take it one step further and make a command line tool for translating text into Morse code? Either way, I hope you’re having fun with this!

Mocking Python print() in Unit Tests

Nowadays, it’s expected that you ship code that meets high quality standards. If you aspire to become a professional, you must learn how to test your code.

Software testing is especially important in dynamically typed languages, such as Python, which don’t have a compiler to warn you about obvious mistakes. Defects can make their way to the production environment and remain dormant for a long time, until that one day when a branch of code finally gets executed.

Sure, you have linters, type checkers, and other tools for static code analysis to assist you. But they won’t tell you whether your program does what it’s supposed to do on the business level.

So, should you be testing print()? No. After all, it’s a built-in function that must have already gone through a comprehensive suite of tests. What you want to test, though, is whether your code is calling print() at the right time with the expected parameters. That’s known as a comportement.

You can test behaviors by mocking real objects or functions. In this case, you want to mock print() to record and verify its invocations.

Mocking in Python can be done twofold. First, you can take the traditional path of statically-typed languages by employing dependency injection. This may sometimes require you to change the code under test, which isn’t always possible if the code is defined in an external library:

def Télécharger(URL, log=impression):
    log(F'Downloading URL')
    # ...

This is the same example I used in an earlier section to talk about function composition. It basically allows for substituting print() with a custom function of the same interface. To check if it prints the right message, you have to intercept it by injecting a mocked function:

>>>

>>> def mock_print(message):
...     mock_print.last_message = message
...
>>> Télécharger('resource', mock_print)
>>> assert 'Downloading resource' == mock_print.last_message

Calling this mock makes it save the last message in an attribute, which you can inspect later, for example in an assert statement.

In a slightly alternative solution, instead of replacing the entire print() function with a custom wrapper, you could redirect the standard output to an in-memory file-like stream of characters:

>>>

>>> def Télécharger(URL, flux=Aucun):
...     impression(F'Downloading URL', déposer=flux)
...     # ...
...
>>> importer io
>>> memory_buffer = io.StringIO()
>>> Télécharger('app.js', memory_buffer)
>>> Télécharger('style.css', memory_buffer)
>>> memory_buffer.getvalue()
'Downloading app.jsnDownloading style.cssn'

This time the function explicitly calls print(), but it exposes its déposer parameter to the outside world.

However, a more Pythonic way of mocking objects takes advantage of the built-in mock module, which uses a technique called monkey patching. This derogatory name stems from it being a “dirty hack” that you can easily shoot yourself in the foot with. It’s less elegant than dependency injection but definitely quick and convenient.

What monkey patching does is alter implementation dynamically at runtime. Such a change is visible globally, so it may have unwanted consequences. In practice, however, patching only affects the code for the duration of test execution.

To mock print() in a test case, you’ll typically use the @patch decorator and specify a target for patching by referring to it with a fully qualified name, that is including the module name:

de unittest.mock importer pièce

@patch('builtins.print')
def test_print(mock_print):
    impression('not a real print')
    mock_print.assert_called_with('not a real print')

This will automatically create the mock for you and inject it to the test function. However, you need to declare that your test function accepts a mock now. The underlying mock object has lots of useful methods and attributes for verifying behavior.

Did you notice anything peculiar about that code snippet?

Despite injecting a mock to the function, you’re not calling it directly, although you could. That injected mock is only used to make assertions afterward and maybe to prepare the context before running the test.

In real life, mocking helps to isolate the code under test by removing dependencies such as a database connection. You rarely call mocks in a test, because that doesn’t make much sense. Rather, it’s other pieces of code that call your mock indirectly without knowing it.

Here’s what that means:

de unittest.mock importer pièce

def greet(Nom):
    impression(F'Hello, Nom!')

@patch('builtins.print')
def test_greet(mock_print):
    greet('John')
    mock_print.assert_called_with('Hello, John!')

The code under test is a function that prints a greeting. Even though it’s a fairly simple function, you can’t test it easily because it doesn’t return a value. It has a side-effect.

To eliminate that side-effect, you need to mock the dependency out. Patching lets you avoid making changes to the original function, which can remain agnostic about print(). It thinks it’s calling print(), but in reality, it’s calling a mock you’re in total control of.

There are many reasons for testing software. One of them is looking for bugs. When you write tests, you often want to get rid of the print() function, for example, by mocking it away. Paradoxically, however, that same function can help you find bugs during a related process of debugging you’ll read about in the next section.

You can’t monkey patch the impression statement in Python 2, nor can you inject it as a dependency. However, you have a few other options:

  • Use stream redirection.
  • Patch the standard output defined in the sys module.
  • Importer print() du __future__ module.

Let’s examine them one by one.

Stream redirection is almost identical to the example you saw earlier:

>>>

>>> def Télécharger(URL, flux=Aucun):
...     impression >> flux, 'Downloading %s' % URL
...     # ...
...
>>> de StringIO importer StringIO
>>> memory_buffer = StringIO()
>>> Télécharger('app.js', memory_buffer)
>>> Télécharger('style.css', memory_buffer)
>>> memory_buffer.getvalue()
'Downloading app.jsnDownloading style.cssn'

There are only two differences. First, the syntax for stream redirection uses chevron (>>) instead of the déposer argument. The other difference is where StringIO est défini. You can import it from a similarly named StringIO module, or cStringIO for a faster implementation.

Patching the standard output from the sys module is exactly what it sounds like, but you need to be aware of a few gotchas:

de mock importer pièce, call

def greet(Nom):
    impression 'Hello, %s!' % Nom

@patch('sys.stdout')
def test_greet(mock_stdout):
    greet('John')
    mock_stdout.écrivez.assert_has_calls([[[[
       call('Hello, John!'),
       call('n')
    ])

First of all, remember to install the mock module as it wasn’t available in the standard library in Python 2.

Secondly, the impression statement calls the underlying .write() method on the mocked object instead of calling the object itself. That’s why you’ll run assertions against mock_stdout.write.

Finally, a single impression statement doesn’t always correspond to a single call to sys.stdout.write(). In fact, you’ll see the newline character written separately.

The last option you have is importing print() de avenir and patching it:

de __future__ importer print_function
de mock importer pièce

def greet(Nom):
    impression('Hello, %s!' % Nom)

@patch('__builtin__.print')
def test_greet(mock_print):
    greet('John')
    mock_print.assert_called_with('Hello, John!')

Again, it’s nearly identical to Python 3, but the print() function is defined in the __builtin__ module rather than builtins.

Thread-Safe Printing

I briefly touched upon the thread safety issue before, recommending enregistrement au dessus de print() fonction. If you’re still reading this, then you must be comfortable with the concept of threads.

Thread safety means that a piece of code can be safely shared between multiple threads of execution. The simplest strategy for ensuring thread-safety is by sharing immutable objects only. If threads can’t modify an object’s state, then there’s no risk of breaking its consistency.

Another method takes advantage of local memory, which makes each thread receive its own copy of the same object. That way, other threads can’t see the changes made to it in the current thread.

But that doesn’t solve the problem, does it? You often want your threads to cooperate by being able to mutate a shared resource. The most common way of synchronizing concurrent access to such a resource is by locking il. This gives exclusive write access to one or sometimes a few threads at a time.

However, locking is expensive and reduces concurrent throughput, so other means for controlling access have been invented, such as atomic variables ou la compare-and-swap algorithm.

Printing isn’t thread-safe in Python. Le print() function holds a reference to the standard output, which is a shared global variable. In theory, because there’s no locking, a context switch could happen during a call to sys.stdout.write(), intertwining bits of text from multiple print() calls.

In practice, however, that doesn’t happen. No matter how hard you try, writing to the standard output seems to be atomic. The only problem that you may sometimes observe is with messed up line breaks:

[Thread-3 A][Thread-2 A][Thread-1 A]

[Thread-3 B][Thread-1 B]


[Thread-1 C][Thread-3 C]

[Thread-2 B]
[Thread-2 C]

To simulate this, you can increase the likelihood of a context switch by making the underlying .write() method go to sleep for a random amount of time. Comment? By mocking it, which you already know about from an earlier section:

importer sys

de temps importer dormir
de random importer random
de filetage importer current_thread, Fil de discussion
de unittest.mock importer pièce

écrivez = sys.stdout.écrivez

def slow_write(texte):
    dormir(random())
    écrivez(texte)

def task():
    thread_name = current_thread().Nom
    pour letter dans 'ABC':
        impression(F'[[[[thread_name letter]')

avec pièce('sys.stdout') comme mock_stdout:
    mock_stdout.écrivez = slow_write
    pour _ dans range(3):
        Fil de discussion(target=task).début()

First, you need to store the original .write() method in a variable, which you’ll delegate to later. Then you provide your fake implementation, which will take up to one second to execute. Each thread will make a few print() calls with its name and a letter: A, B, and C.

If you read the mocking section before, then you may already have an idea of why printing misbehaves like that. Nonetheless, to make it crystal clear, you can capture values fed into your slow_write() fonction. You’ll notice that you get a slightly different sequence each time:

[[[[
    '[Thread-3 A]',
    '[Thread-2 A]',
    '[Thread-1 A]',
    'n',
    'n',
    '[Thread-3 B]',
    (...)
]

Even though sys.stdout.write() itself is an atomic operation, a single call to the print() function can yield more than one write. For example, line breaks are written separately from the rest of the text, and context switching takes place between those writes.

You can make the newline character become an integral part of the message by handling it manually:

impression(F'[[[[thread_name letter]n', finir='')

This will fix the output:

[Thread-2 A]
[Thread-1 A]
[Thread-3 A]
[Thread-1 B]
[Thread-3 B]
[Thread-2 B]
[Thread-1 C]
[Thread-2 C]
[Thread-3 C]

Notice, however, that the print() function still keeps making a separate call for the empty suffix, which translates to useless sys.stdout.write('') instruction:

[[[[
    '[Thread-2 A]n',
    '[Thread-1 A]n',
    '[Thread-3 A]n',
    '',
    '',
    '',
    '[Thread-1 B]n',
    (...)
]

A truly thread-safe version of the print() function could look like this:

importer filetage

fermer à clé = filetage.Fermer à clé()

def thread_safe_print(*args, **kwargs):
    avec fermer à clé:
        impression(*args, **kwargs)

You can put that function in a module and import it elsewhere:

de thread_safe_print importer thread_safe_print

def task():
    thread_name = current_thread().Nom
    pour letter dans 'ABC':
        thread_safe_print(F'[[[[thread_name letter]')

Now, despite making two writes per each print() request, only one thread is allowed to interact with the stream, while the rest must wait:

[[[[
    # Lock acquired by Thread-3 
    '[Thread-3 A]',
    'n',
    # Lock released by Thread-3
    # Lock acquired by Thread-1
    '[Thread-1 B]',
    'n',
    # Lock released by Thread-1
    (...)
]

I added comments to indicate how the lock is limiting access to the shared resource.

Conversely, the enregistrement module is thread-safe by design, which is reflected by its ability to display thread names in the formatted message:

>>>

>>> importer enregistrement
>>> enregistrement.basicConfig(format='%(threadName)s %(message)s')
>>> enregistrement.Erreur('hello')
MainThread hello

It’s another reason why you might not want to use the print() function all the time.

Python Print Counterparts

By now, you know a lot of what there is to know about print()! The subject, however, wouldn’t be complete without talking about its counterparts a little bit. Tandis que print() is about the output, there are functions and libraries for the input.

Built-In

Python comes with a built-in function for accepting input from the user, predictably called input(). It accepts data from the standard input stream, which is usually the keyboard:

>>>

>>> Nom = input('Enter your name: ')
Enter your name: jdoe
>>> impression(Nom)
jdoe

The function always returns a string, so you might need to parse it accordingly:

essayer:
    âge = int(input('How old are you? '))
except ValueError:
    passe

The prompt parameter is completely optional, so nothing will show if you skip it, but the function will still work:

>>>

>>> X = input()
hello world
>>> impression(X)
hello world

Nevertheless, throwing in a descriptive call to action makes the user experience so much better.

Asking the user for a password with input() is a bad idea because it’ll show up in plaintext as they’re typing it. In this case, you should be using the getpass() function instead, which masks typed characters. This function is defined in a module under the same name, which is also available in the standard library:

>>>

>>> de getpass importer getpass
>>> password = getpass()
Password: 
>>> impression(password)
s3cret

Le getpass module has another function for getting the user’s name from an environment variable:

>>>

>>> de getpass importer getuser
>>> getuser()
'jdoe'

Python’s built-in functions for handling the standard input are quite limited. At the same time, there are plenty of third-party packages, which offer much more sophisticated tools.

Third-Party

There are external Python packages out there that allow for building complex graphical interfaces specifically to collect data from the user. Some of their features include:

  • Advanced formatting and styling
  • Automated parsing, validation, and sanitization of user data
  • A declarative style of defining layouts
  • Interactive autocompletion
  • Mouse support
  • Predefined widgets such as checklists or menus
  • Searchable history of typed commands
  • Syntax highlighting

Demonstrating such tools is outside of the scope of this article, but you may want to try them out. I personally got to know about some of those through the Python Bytes Podcast. Here they are:

Nonetheless, it’s worth mentioning a command line tool called rlwrap that adds powerful line editing capabilities to your Python scripts for free. You don’t have to do anything for it to work!

Let’s assume you wrote a command-line interface that understands three instructions, including one for adding numbers:

impression('Type "help", "exit", "add a [b [c ...]]"')
tandis que Vrai:
    command, *arguments = input('~ ').diviser(' ')
    si len(command) > 0:
        si command.plus bas() == 'exit':
            Pause
        elif command.plus bas() == 'help':
            impression('This is help.')
        elif command.plus bas() == 'add':
            impression(sum(carte(int, arguments)))
        autre:
            impression('Unknown command')

At first glance, it seems like a typical prompt when you run it:

$ python calculator.py
Type "help", "exit", "add a [b [c ...]]"
~ add 1 2 3 4
dix
~ aad 2 3
Unknown command
~ exit
$

But as soon as you make a mistake and want to fix it, you’ll see that none of the function keys work as expected. Hitting the Gauche arrow, for example, results in this instead of moving the cursor back:

$ python calculator.py
Type "help", "exit", "add a [b [c ...]]"
~ aad^[[D[[D[[D[[D

Now, you can wrap the same script with the rlwrap commander. Not only will you get the arrow keys working, but you’ll also be able to search through the persistent history of your custom commands, use autocompletion, and edit the line with shortcuts:

$ rlwrap python calculator.py
Type "help", "exit", "add a [b [c ...]]"
(reverse-i-search)`a': add 1 2 3 4

Isn’t that great?

Conclusion

You’re now armed with a body of knowledge about the print() function in Python, as well as many surrounding topics. You have a deep understanding of what it is and how it works, involving all of its key elements. Numerous examples gave you insight into its evolution from Python 2.

Apart from that, you learned how to:

  • Avoid common mistakes with print() in Python
  • Deal with newlines, character encodings and buffering
  • Write text to files
  • Mock the print() function in unit tests
  • Build advanced user interfaces in the terminal

Now that you know all this, you can make interactive programs that communicate with users or produce data in popular file formats. You’re able to quickly diagnose problems in your code and protect yourself from them. Last but not least, you know how to implement the classic snake game.

If you’re still thirsty for more information, have questions, or simply would like to share your thoughts, then feel free to reach out in the comments section below.

Commentaires

Laisser un commentaire

Votre commentaire sera révisé par les administrateurs si besoin.