% Programmation avec Python (chapitre 9) % Dimitri Merejkowsky
\center \huge Formats et chaînes de caractères
Problème:
\vfill
>>> nom = "Ford"
>>> résultat = 42
>>> message = "Bonjour, " + nom + ". "
>>> message += "La réponse est: " + str(résultat) + "."
>>> message
'Bonjour, Ford. La réponse est: 42.'
\vfill
Ce n’est pas très lisible ...
3 solutions différentes en Python
%
printf
en C par example)name = "John"
print("Bonjour %s!" % name) # 's' comme string
age = 42
print("Vous avez %i ans" % 42) # 'i' comme integer
>>> print("Bonjour %i!" % name)
TypeError: %i format: a number is required, not str
Avec un tuple:
print("Bonjour %s, vous avez %i" % (name, age))
\vfill
Avec un dictionnaire:
data = {"name": "John", "age": 42}
print("Bonjour %(name)s, vous avez %(age)i ans" % data)
Des {}
comme “placeholders” et une méthode sur les strings:
>>> nom = "Ford"
>>> résultat = 42
>>> template = "Bonjour, {}. La réponse est: {}"
>>> message = template.format(nom, résultat)
>>> message
'Bonjour, Ford. La réponse est: 42.'
On peut aussi nommer les remplacements:
template = "Bonjour, {nom}. La réponse est: {résultat}"
template.format(nom="Ford", résultat=42)
On peut les ordonner:
template = "Bonjour {1}. La réponse est {0}"
template.format(reponse, name)
%
!format()
%
et .format()
On peut mettre du code dans {}
avec la lettre f
devant la chaîne:
name = "Patrick"
score = 42
text = f"Bonjour {name}. Votre score est: {score}"
\vfill
Mais aussi:
text = f"2 + 2 égale { 2 + 2 }"
Je vous ai présenté %
et format()
parce que vous risquez d’en voir.
Mais si vous avez le choix, utilisez des f-strings
!
>>> pi = 3.14159265359
>>> truncated = "%.2f" % pi
3.14
Le texte dans les accolades après le :
est un mini-langage de spécification de format.
.2
veut dire: 2 chiffres après la virgule maximum.
On peut aussi faire des alignements et du “padding”:
\vfill
template = "{name:>10}: {score:03}"
print(template.format(name="Alice", score=42))
print(template.format(name="Bob", score=5))
Alice: 042
Bob: 005
>10
: aligné à gauche, taille minimum 1003
: rajouter jusqu'à 2 zéros à gauche pour que la taille fasse 3Documentation ici:
htps://docs.python.org/fr/3/library/string.html#format-specification-mini-language
\center \huge Rappels sur les classes
class Car:
total_number_of_cars = 0
def __init__(self, color="black"):
self.color = color
Car.total_number_of_cars += 1
def drive(self):
print("vroom")
@classmethod
def print_number_of_cars(cls):
print(cls.total_number_of_cars,
"cars have been made")
class Authorization:
def __init__(self, credentials_file):
...
self.password = ...
class Client:
url = "https://exmple.com"
def __init__(self, auth)
self.auth = auth
def make_request(self):
password = self.auth.get_password()
requests.get(url, password=password)
class A:
def method_in_a(self):
self.attribute_in_a = 42
class B(A):
def method_in_b(self):
self.method_in_a() # ok
self.attribute_in_a # ok
class A:
def method_in_a(self):
pass
class B(A):
def method_in_b(self):
pass
>>> a = A()
>>> a.method_in_a() # ok
>>> a.method_in_b() # error
>>> b = B()
>>> b.method_in_b() # ok
>>> b.method_in_a() # ok
class A:
def method_in_a(self):
pass
class B(A):
def method_in_b(self):
pass
>>> a = A()
>>> a.method_in_a() # ok
>>> a.method_in_b() # error
>>> b = B()
>>> b.method_in_b() # ok
>>> b.method_in_a() # ok
class A:
def method(self):
print("A!")
class B(A):
def method(self):
print("B!")
>>> a = A()
>>> a.method() # ok
'A!'
>>> b = B()
>>> b.method()
'B!'
class A:
def method(self):
print("A!")
class B(A):
def method(self):
super().method()
print("B!")
>>> a = A()
>>> a.method() # ok
'A!'
>>> b = B()
>>> b.method()
'A!'
'B!'
# All animals have a species
class Animal:
def __init__(self, species):
self.species = species
# Pets are animals with a name
class Pet(Animal):
def __init__(self, species, name):
super().__init__(species) # <- à ne pas oublier
self.name = name
# All dogs are pets
class Dog(Pet):
def __init__(self, name):
super().init("dog", name)
\center \huge Interfaces et classes abstraites
Imaginons un jeu où il faut retrouver le nom d’un super-héros à partir de sa description.
On a une classe MarvelClient
qui permet de lister les personnages et leurs
descriptions et une class Game
pour la logique du jeu
class MarvelClient:
url = "https://marvel.com/api"
def __init__(self, credentials_file):
# réupére les clés depuis un fichier
def get_all_characters(self):
# appelle l'api marvel pour récupérer
# tous les personnages
def get_description(self, character_name)
# appelle l'api marvel pour récupérer
# une description
import random
class Game:
def __init__(self, marvel_client)
self.auth = marvel_client
def play(self):
characters = self.marvel_client.get_all_characters()
name_to_guess = random.choice(characters)
description = self.marvel_client.get_description(
name_to_guess)
while not self.won():
...
Il y a un contrat implicite entre Game
et MarvelClient
.
Dans play
on appelle self.marvel_client.get_all_characters()
donc la méthode
get_all_characters()
doit:
Pareil avec get_description()
. La méthode doit:
On peut passer à Client.__init__()
n’importe qu’elle classe pourvu qu’elle ait
les bonnes méthodes!
On appelle ça “duck typing”
Définition traditionnelle (pas exacte à mon sens):
\vfill
Meilleure définition:
class FakeCLient():
def get_all_characters(self):
return ["Spider-Man", "Batman", "Superman"]
def get_description(self, name):
if name == "Spider-Man":
...
...
fake_client = FakeMarvelClient()
game = Game(fake_client)
game.play()
# Tout marche!