Génération efficace de classes
Maintenant que vous savez comment créer des classes en utilisant la fonction type()
, nous allons explorer une méthode plus efficace pour générer plusieurs classes similaires. Cette méthode vous fera gagner du temps et réduira la duplication de code, rendant votre processus de programmation plus fluide.
Compréhension des classes de validateurs actuelles
Tout d'abord, nous devons ouvrir le fichier validate.py
dans WebIDE. Ce fichier contient déjà plusieurs classes de validateurs, qui sont utilisées pour vérifier si les valeurs répondent à certaines conditions. Ces classes incluent Validator
, Positive
, PositiveInteger
et PositiveFloat
. Nous allons ajouter une classe de base Typed
et plusieurs validateurs spécifiques à un type à ce fichier.
Pour ouvrir le fichier, exécutez la commande suivante dans le terminal :
cd ~/project
Ajout de la classe de validateur Typed
Commençons par ajouter la classe de validateur Typed
. Cette classe sera utilisée pour vérifier si une valeur est du type attendu.
class Typed(Validator):
expected_type = object ## Default, will be overridden in subclasses
@classmethod
def check(cls, value):
if not isinstance(value, cls.expected_type):
raise TypeError(f'Expected {cls.expected_type}')
super().check(value)
Dans ce code, expected_type
est défini sur object
par défaut. Les sous - classes l'écraseront avec le type spécifique qu'elles vérifient. La méthode check
utilise la fonction isinstance
pour vérifier si la valeur est du type attendu. Sinon, elle lève une erreur TypeError
.
Traditionnellement, nous créerions des validateurs spécifiques à un type comme ceci :
class Integer(Typed):
expected_type = int
class Float(Typed):
expected_type = float
class String(Typed):
expected_type = str
Cependant, cette approche est répétitive. Nous pouvons faire mieux en utilisant le constructeur type()
pour générer ces classes dynamiquement.
Génération dynamique de validateurs de type
Nous allons remplacer les définitions de classes individuelles par une approche plus efficace.
_typed_classes = [
('Integer', int),
('Float', float),
('String', str)
]
globals().update((name, type(name, (Typed,), {'expected_type': ty}))
for name, ty in _typed_classes)
Voici ce que ce code fait :
- Il définit une liste de tuples. Chaque tuple contient un nom de classe et le type Python correspondant.
- Il utilise une expression génératrice avec la fonction
type()
pour créer chaque classe. La fonction type()
prend trois arguments : le nom de la classe, un tuple de classes de base et un dictionnaire d'attributs de classe.
- Il utilise
globals().update()
pour ajouter les classes nouvellement créées à l'espace de noms global. Cela rend les classes accessibles dans tout le module.
Votre fichier validate.py
terminé devrait ressembler à ceci :
## Basic validator classes
class Validator:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.name]
def __set__(self, instance, value):
self.check(value)
instance.__dict__[self.name] = value
@classmethod
def check(cls, value):
pass
class Positive(Validator):
@classmethod
def check(cls, value):
if value <= 0:
raise ValueError('Expected a positive value')
super().check(value)
class PositiveInteger(Positive):
@classmethod
def check(cls, value):
if not isinstance(value, int):
raise TypeError('Expected an integer')
super().check(value)
class PositiveFloat(Positive):
@classmethod
def check(cls, value):
if not isinstance(value, float):
raise TypeError('Expected a float')
super().check(value)
class Typed(Validator):
expected_type = object ## Default, will be overridden in subclasses
@classmethod
def check(cls, value):
if not isinstance(value, cls.expected_type):
raise TypeError(f'Expected {cls.expected_type}')
super().check(value)
## Generate type validators dynamically
_typed_classes = [
('Integer', int),
('Float', float),
('String', str)
]
globals().update((name, type(name, (Typed,), {'expected_type': ty}))
for name, ty in _typed_classes)
Test des classes générées dynamiquement
Maintenant, testons nos classes de validateurs générées dynamiquement. Tout d'abord, ouvrez un shell interactif Python.
cd ~/project
python3
Une fois que vous êtes dans le shell Python, importez et testez nos validateurs.
from validate import Integer, Float, String
## Test the Integer validator
i = Integer()
i.__set_name__(None, 'test_int')
try:
i.check("not an integer")
print("Error: Check passed when it should have failed")
except TypeError as e:
print(f"Integer validation: {e}")
## Test the String validator
s = String()
s.__set_name__(None, 'test_str')
try:
s.check(123)
print("Error: Check passed when it should have failed")
except TypeError as e:
print(f"String validation: {e}")
## Add a new validator class to the list
import sys
print("Current validator classes:", [cls for cls in dir() if cls in ['Integer', 'Float', 'String']])
Vous devriez voir une sortie montrant les erreurs de validation de type. Cela indique que nos classes générées dynamiquement fonctionnent correctement.
Lorsque vous avez terminé de tester, quittez le shell Python :
exit()
Extension de la génération dynamique de classes
Si vous souhaitez ajouter plus de validateurs de type, vous pouvez simplement mettre à jour la liste _typed_classes
dans validate.py
.
_typed_classes = [
('Integer', int),
('Float', float),
('String', str),
('List', list),
('Dict', dict),
('Bool', bool)
]
Cette approche offre un moyen puissant et efficace de générer plusieurs classes similaires sans écrire de code répétitif. Elle vous permet de mettre à l'échelle facilement votre application à mesure que vos besoins augmentent.