Ceci est un texte traduit automatiquement qui peut contenir des erreurs !
En Python (cela s’applique également à la plupart des autres langages), il est possible de créer ses propres objets, avec leurs propres valeurs, règles et fonctions. Cela s’appelle des classes (classes en anglais). Nous utilisons des classes pour regrouper des données et des fonctions connexes en une seule unité, par exemple une classe Ordre qui contient des données telles que ordre_id, kunde_navn, produkter et des fonctions telles que legg_til_produkt(), beregn_total(), etc.
Dictionary (JSON) vs Klasser (Objekter)
JSON (JavaScript Object Notation) est un format pour stocker et transférer des données indépendamment du langage de programmation, tandis qu’une classe est une structure dans un langage de programmation spécifique.
Lorsque nous devons envoyer des données sur le réseau, ou les stocker dans un fichier, nous utilisons souvent JSON (ou des tables dans des bases de données).
Lorsque nous devons travailler avec des données structurées dans le code, nous utilisons des classes.
Dans ce module, nous allons examiner comment utiliser les classes pour valider les données.
Le plus simple est d’utiliser @dataclass de la bibliothèque dataclasses. Cela nous évite d’écrire beaucoup de code boilerplate pour créer une classe. (Comme par exemple les fonctions intégrées __init__ et __repr__ (représentation)).
Exemple d’une classe sans utiliser le décorateur dataclass :
class Car:
def __init__(self, make: str, model: str, year: int):
self.make = make
self.model = model
self.year = year
def __repr__(self):
return f"{self.year} {self.make} {self.model}"
my_car = Car("Toyota", "Corolla", 2020)
print(my_car) # Sortie : 2020 Toyota Corolla
Exemple avec le décorateur dataclass, qui permet d’obtenir le même résultat que ci-dessus (mais avec moins de code) :
from dataclasses import dataclass
@dataclass
class Car:
make: str
model: str
year: int
my_car = Car("Toyota", "Corolla", 2020)
print(my_car) # Sortie : Car(make='Toyota', model='Corolla', year=2020)
Exercice 1 - Créer une classe
Créez une classe nommée Person. Cette classe doit avoir les attributs suivants :
name: Le nom de la personneeye_color: La couleur des yeux de la personnephone_number: Le numéro de téléphone de la personneemail: L’adresse e-mail de la personne
Instanciez (utilisez) un objet de la classe Person avec des valeurs valides pour tous les attributs, comme dans l’exemple ci-dessous.
@dataclass
class Person:
... # Votre code ici
bob_kaare = Person(name="Bob Kåre",
eye_color="blue",
phone_number="12345678",
email="bob_kaare@example.com")
print(bob_kaare)
Løsning: En dataclass for Person
Voici une solution possible :
from dataclasses import dataclass
@dataclass
class Person:
name: str
eye_color: str
phone_number: str
email: str
Exercice 2 - Validation dans la classe
Dans l’exemple ci-dessus, nous n’avons pas ajouté de validation. Cela signifie que nous pouvons créer une Personne avec des valeurs invalides, telles que :
@dataclass
class Person:
... # Votre code ici
invalid_person = Person(name="",
eye_color="yes",
phone_number="12345",
email="not-an-email")
print(invalid_person)
# Output: Person(name='', eye_color='yes', phone_number='12345', email='not-an-email')
Ceci est (potentiellement) problématique et peut facilement entraîner une dette technique à l’avenir. Heureusement, il existe des moyens simples d’ajouter une validation aux classes.
Nous allons commencer par examiner la validation des adresses e-mail. Il existe des bibliothèques intégrées en Python qui peuvent nous aider à ce faire, mais comme nous faisons cela pour apprendre, nous allons créer notre propre validation simple en créant une nouvelle classe “Email” et en examinant une fonction __post_init__ (uniquement pour dataclass).
Merk
Nous pouvons également faire cela dans la classe Person elle-même, mais il est souvent préférable de créer des classes distinctes pour les éléments qui peuvent être réutilisés.
Exemple d’une fonction post_init
from dataclasses import dataclass
@dataclass
class Email:
address: str
def __post_init__(self):
print(f"Validating email: {self.address}")
# Votre code ici
Pour une simple validation d’e-mail, nous pouvons par exemple vérifier que l’e-mail contient à la fois les caractères @ et .. Éventuellement, vous pouvez vérifier que l’e-mail correspond à un modèle regex. (Plus avancé, mais n’hésitez pas à chercher sur le web !)
Løsning: Kode for enkel e-post validering
Voici une solution possible, nous utilisons des exceptions pour “planter” le programme si l’e-mail est invalide, ce qui arrêtera le programme immédiatement et affichera un message d’erreur.
from dataclasses import dataclass
@dataclass
class Email:
address: str
def __post_init__(self):
if "@" not in self.address:
raise ValueError(f"Mangler @ i epostadressen: {self.address}")
if "." not in self.address.split("@")[1]:
raise ValueError(f"Mangler . i domenet til epostadressen: {self.address}")
if " " in self.address:
raise ValueError(f"Epostadressen kan ikke inneholde mellomrom: {self.address}")
# Test koden
test = Email("hei@example.com") # Gyldig
try:
test = Email("heiexample.com")
except ValueError as e:
print(e) # Ugyldig, mangler @
Exercice 3 - Validation des numéros de téléphone
Créez une classe similaire à celle que vous avez faite pour les adresses e-mail, mais maintenant pour les numéros de téléphone.
Problème avec la validation téléphonique !
Pouvez-vous corriger la validation des numéros de téléphone pour qu’elle accepte à la fois les lettres (str) et les chiffres (int) ? Par exemple, 12345678 et "12345678" doivent tous deux être valides.
Essayez également d’ajouter les indicatifs de pays comme un attribut (sous-valeur de la classe). Par exemple, 47 pour la Norvège, 46 pour la Suède
Exercice 4 - Utiliser la validation dans la classe Person
Maintenant que nous avons créé la validation pour l’e-mail et le numéro de téléphone, nous pouvons les utiliser dans la classe Person.
@dataclass
class Person:
name: str
eye_color: str
phone_number: PhoneNumber # Utilisez la classe PhoneNumber
email: Email # Utilisez la classe Email
Nouveau défi se présente !
Maintenant que nous avons modifié la classe Person pour utiliser les classes PhoneNumber et Email, nous devons également modifier la façon dont nous instancions (créons) une Person. Nous devons maintenant d’abord créer un objet PhoneNumber et un objet Email, avant de pouvoir créer une Person.
bob_kaare = Person(name="Bob Kåre",
eye_color="blue",
phone_number=PhoneNumber("12345678"), # Remarquez le changement ici
email=Email("bob_kaare@example.com")) # Remarquez le changement ici
print(bob_kaare)
# Remarque : une modification doit être apportée à la manière dont nous récupérons les valeurs également
print(bob_kaare.email.address)
print(bob_kaare.phone_number.number) # .country_code(?)
Exercice 5 - Propriétés dans les classes (Facultatif)
Lorsque nous utilisons des objets pour représenter des valeurs comme les adresses e-mail et les numéros de téléphone, nous sommes obligés de spécifier la sous-valeur (par exemple, address pour l’e-mail et number pour le numéro de téléphone) chaque fois que nous voulons extraire la valeur. Cela peut devenir un peu lourd à la longue. Heureusement, il existe une solution à cela, en utilisant le décorateur @property dans une classe, ce qui nous permet d’extraire la valeur directement de l’objet, sans avoir à spécifier la sous-valeur.
Cela pose cependant un autre défi, et c’est que nous avons besoin de la fonction __init__ dans la classe Person. Cela est dû au fait que nous ne pouvons pas utiliser le même nom à la fois pour une propriété et un attribut dans une dataclass.
from dataclasses import dataclass
@dataclass
class EksempelVerdi:
attributt: str
@dataclass
class Person:
name: str
_verdi: EksempelVerdi # Variable interne (commence par _ pour indiquer qu'elle est "privée")
def __init__(self, name: str, verdi: EksempelVerdi):
self.name = name
self._verdi = verdi
@property
def verdi(self):
return self._verdi.attributt # Récupère la sous-valeur directement
# Test du code
person = Person(name="Alice", verdi=EksempelVerdi("Noe tekst"))
print(person.verdi) # Output: Noe tekst
Merk
Les propriétés sont uniques en ce sens qu’elles n’ont pas besoin de paramètres et n’ont pas besoin de parenthèses pour être exécutées. Dans l’exemple, nous récupérons person.verdi sans parenthèses (pas person.verdi()), même s’il s’agit techniquement d’une fonction.
Exemple alternatif qui accepte à la fois str et EksempelVerdi
@dataclass
class Person:
name: str
_verdi: str
def __init__(self, name: str, verdi: str | EksempelVerdi):
self.name = name
if isinstance(verdi, EksempelVerdi):
self._verdi = verdi
elif isinstance(verdi, str):
self._verdi = EksempelVerdi(verdi)
else:
raise TypeError("verdi må være av type str eller EksempelVerdi")
@property
def verdi(self) -> str:
return self._verdi.attributt # Henter ut underverdien direkte
# Test koden
person = Person(name="Alice", verdi="Noe tekst")
print(person.verdi) # Output: Noe tekst
Exercice 6 - Propriétés avec logique (Facultatif)
Mettez à jour Person en ajoutant un nouvel attribut appelé birthday (date de naissance). Celui-ci doit être de type datetime.date (depuis la bibliothèque datetime).
Créez ensuite les propriétés suivantes :
- Créez une propriété
agequi calcule l’âge de la personne en fonction debirthdayet de la date actuelle. - Créez une propriété
is_adultqui renvoieTruesi la personne a 18 ans ou plus, sinonFalse.
Solution : Propriétés Age et Adulte
Voici une solution possible :
from dataclasses import dataclass
from datetime import date
@dataclass
class Person:
name: str
birthday: date
@property
def age(self) -> int:
"""Calcule l'âge en fonction de la date de naissance et de la date d'aujourd'hui"""
today = date.today()
age = today.year - self.birthday.year
# Ajuster vers le bas de 1 si la personne n'a pas encore eu son anniversaire cette année
if (today.month, today.day) < (self.birthday.month, self.birthday.day):
age -= 1
return age
@property
def is_adult(self) -> bool:
"""Retourne True si la personne a 18 ans ou plus"""
return self.age >= 18
# Tester le code
person = Person(name="Alice", birthday=date(2005, 5, 15))
print(person.age) # Par exemple, 18 si la date d'aujourd'hui est après le 15 mai 2023
print(person.is_adult) # True
Encore un défi !
Pouvez-vous faire en sorte que l’instanciation de Person accepte à la fois datetime.date et une chaîne de texte au format "JJ-MM-AAAA" pour birthday ? (Indice : utilisez datetime.strptime pour convertir la chaîne en datetime.date)
