Le modèle Strategy est votre ami ici. Je vais également aborder quelques autres façons de nettoyer le code.
Vous pouvez lire à propos du modèle de stratégie ici : https://en.wikipedia.org/wiki/Strategy_pattern
Vous avez dit "Comme vous pouvez le voir, chaque méthode surchargée est une copie/collage de la précédente avec juste de petits changements." C'est un signe pour utiliser ce modèle. Si vous pouvez transformer le petit changement en une fonction, alors vous pouvez écrire une seule fois le code de base et vous concentrer sur les parties intéressantes.
class Vector:
def _arithmitize(self, other, f, error_msg):
if isinstance(other, int) or isinstance(other, float):
tmp = list()
for a in self.l:
tmp.append(func(a, other))
return Vector(tmp)
raise ValueError(error_msg)
def _err_msg(self, op_name):
return "Nous ne pouvons que {} un vecteur par un scalaire".format(opp_name)
def __mul__(self, other):
return self._arithmitize(
other,
lambda a, b: a * b,
self._err_msg('mul'))
def __div__(self, other):
return self._arithmitize(
other,
lambda a, b: a / b,
self._err_msg('div'))
# et ainsi de suite ...
Nous pouvons encore nettoyer un peu plus avec une compréhension de liste
class Vector:
def _arithmetize(self, other, f, error_msg):
if isinstance(other, int) or isinstance(other, float):
return Vector([f(a, other) for a in self.l])
raise ValueError(error_msg)
def _err_msg(self, op_name):
return "Nous ne pouvons que {} un vecteur par un scalaire".format(opp_name)
def __mul__(self, other):
return self._arithmetize(
other,
lambda a, b: a * b,
self._err_msg('mul'))
def __div__(self, other):
return self._arithmetize(
other,
lambda a, b: a / b,
self._err_msg('div'))
Nous pouvons améliorer la vérification des types
import numbers
class Vector:
def _arithmetize(self, other, f, error_msg):
if isinstance(other, number.Numbers):
return Vector([f(a, other) for a in self.l])
raise ValueError(error_msg)
Nous pouvons utiliser des opérateurs au lieu d'écrire des lambdas :
import operators as op
class Vector:
# snip ...
def __mul__(self, other):
return self._arithmetize(other, op.mul, self._err_msg('mul'))
Donc au final, nous obtenons quelque chose comme cela :
import numbers
import operators as op
class Vector(object):
def _arithmetize(self, other, f, err_msg):
if isinstance(other, numbers.Number):
return Vector([f(a, other) for a in self.l])
raise ValueError(self._error_msg(err_msg))
def _error_msg(self, msg):
return "Nous ne pouvons que {} un vecteur par un scalaire".format(opp_name)
def __mul__(self, other):
return self._arithmetize(op.mul, other, 'mul')
def __truediv__(self, other):
return self._arithmetize(op.truediv, other, 'truediv')
def __floordiv__(self, other):
return self._arithmetize(op.floordiv, other, 'floordiv')
def __mod__(self, other):
return self._arithmetize(op.mod, other, 'mod')
def __pow__(self, other):
return self._arithmetize(op.pow, other, 'pow')
Il y a d'autres moyens de générer dynamiquement cela, mais pour un petit ensemble de fonctions comme celui-ci, la lisibilité compte.
Si vous devez générer cela dynamiquement, essayez quelque chose comme ceci :
class Vector(object):
def _arithmetize(....):
# vous avez déjà vu cela
def __getattr__(self, name):
funcs = {
'__mul__': op.mul, # note: cela peut ne pas fonctionner avec les méthodes d'attributs spéciaux. À vérifier
'__mod__': op.mod,
...
}
def g(self, other):
try:
return self._arithmetize(funcs[name],...)
except:
raise NotImplementedError(...)
return g
Si vous trouvez que cet exemple dynamique ne fonctionne pas, consultez rendre la surcharge d'opérateurs moins redondante en python?, qui traite du cas de la création dynamique de méthodes d'attributs spéciaux dans la plupart des implémentations python.