選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
このリポジトリはアーカイブされています。 ファイルの閲覧とクローンは可能ですが、プッシュや、課題・プルリクエストのオープンはできません。
 
 
 
 
 
 

7.8 KiB

% Programmation avec Python (chapitre 9) % Dimitri Merejkowsky

\center \huge Formats et chaînes de caractères

Formater des 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

Solution 1: À l’ancienne

  • Avec %
  • Compatible avec les très vieux Pythons
  • Compatibles avec d’autres langage (printf en C par example)

Example 1

name = "John"
print("Bonjour %s!" % name)   # 's' comme string

age = 42
print("Vous avez %i ans" % 42)  # 'i' comme integer

Attention aux types:

>>> print("Bonjour %i!" % name)
TypeError: %i format: a number is required, not str

On peut en mettre plusieurs

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)

Solution 2: format()

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.'
  • Pas de types!

format() - à plusieurs

On peut aussi nommer les remplacements:

template = "Bonjour, {nom}. La réponse est: {résultat}"
template.format(nom="Ford", résultat=42)

format() - à plusieurs

On peut les ordonner:

template = "Bonjour {1}. La réponse est {0}"
template.format(reponse, name)
  • Pas possible avec %!

Solution 3: f-strings

  • La meilleure de toutes :)
  • Plus succint que format()
  • Plus performant que % et .format()
  • Mais pas avant Python 3.6

Principe

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 }"

Conclusion

Je vous ai présenté % et format() parce que vous risquez d’en voir.

Mais si vous avez le choix, utilisez des f-strings!

Spécifications de formats

  • Permet des opérations pendant la conversion en texte
  • Fonctionne avec les 3 solutions

Tronquer

>>> 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.

Alignements et paddings

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 10
  • 03: rajouter jusqu'à 2 zéros à gauche pour que la taille fasse 3

Documentation

Documentation ici:

htps://docs.python.org/fr/3/library/string.html#format-specification-mini-language

\center \huge Rappels sur les classes

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")

Composition

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)

Héritage - partage des attributs et méthodes

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

Héritage - ordre de résolution des méthodes

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

Héritage - ordre de résolution des méthodes

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

Héritage: surcharge

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!'

Héritage - super()

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!'

Héritage - super() et __init__

# 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

Example

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

Implémentation - MarvelClient

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

Implémentation - Game

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():
      ...

Contrats implicites - 1

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:

  • exister
  • ne prendre aucun agrument
  • retourner une liste de noms

Contrats implicites - 2

Pareil avec get_description(). La méthode doit:

  • exister
  • prendre un nom en unique argument
  • retourner une description

Une force et une faiblesse

On peut passer à Client.__init__() n’importe qu’elle classe pourvu qu’elle ait les bonnes méthodes!

On appelle ça “duck typing”

duck typing

Définition traditionnelle (pas exacte à mon sens):

  • Si ça marche comme un canard et que ça fait coin-coin comme un canard alors c’est un canard.

\vfill

Meilleure définition:

  • Tu peux passer remplacer le canard par une vache. Tant que la vache a un bec et fait coin-coin, c’est bon!

En image

canard vache

Exmaple utile

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!