J'essaie de contrôler les versions du produit dans des scripts Python pour une raison spécifique, mais je ne pouvais pas comprendre comment le faire de manière élégante - veuillez aider.
Actuellement, je fais quelque chose comme ci-dessous. Cependant, les scripts sont difficiles à gérer lorsque le contenu de la version est modifié.
class Product(object):
def __init__(client):
self.version = client.version # get client version from another module
def function():
if self.version == '1.0':
print('for version 1.0')
Elif self.version == '2.0':
print('for version 2.0')
else:
print(f'function not support {self.version}')
Par conséquent, je veux faire quelque chose comme ce qui suit pour séparer les fonctions du même nom.
class Product(object):
def __init__(client):
self.version = client.version # get client version from another module
def function():
print('for version 1.0')
def function():
print('for version 2.0')
Je pensais utiliser décorateur pour y parvenir:
class Product(object):
def __init__(client):
self.version = client.version # get client version from another module
@version(1.0)
def function():
print('for version 1.0')
@version(2.0)
def function():
print('for version 2.0')
Cependant, je n'ai pas compris comment ... on dirait qu'un décorateur ne peut pas faire cette opération ou que je ne comprends tout simplement pas comment.
Y a-t-il une manière élégante de faire ceci?
L'héritage est probablement le meilleur moyen de le faire, mais puisque vous avez posé une question spécifique sur les décorateurs, je voulais montrer que vous pouvez le faire en utilisant des décorateurs.
Vous devrez utiliser un dictionnaire pour stocker vos fonctions par version, puis rechercher la version à utiliser au moment de l'exécution. Voici un exemple.
version_store = {}
def version(v):
def dec(f):
name = f.__qualname__
version_store[(name, v)] = f
def method(self, *args, **kwargs):
f = version_store[(name, self.version)]
return f(self, *args, **kwargs)
return method
return dec
class Product(object):
def __init__(self, version):
self.version = version
@version("1.0")
def function(self):
print("1.0")
@version("2.0")
def function(self):
print("2.0")
Product("1.0").function()
Product("2.0").function()
Pourriez-vous mettre votre classe Product
dans deux modules, v1 et v2, puis les importer de manière conditionnelle?
Par exemple:
Productv1.py
class Product(object):
def function():
print('for version 1.0')
Productv2.py
class Product(object):
def function():
print('for version 2.0')
Puis dans votre fichier principal:
main.py
if client.version == '1.0':
from Productv1 import Product
Elif client.version == '2.0':
from Productv2 import Product
else:
print(f'function not support {self.version}')
p = Product
p.function()
Une autre option consiste à créer une classe dans une usine.
Créez vos fonctions versionnées (notez le paramètre self
). Cela peut être fait dans un module différent. Ajoutez également une collection pour récupérer la fonction en fonction du numéro de version.
def func_10(self):
print('for version 1.0')
def func_20(self):
print('for version 2.0')
funcs = {"1.0": func_10,
"2.0": func_20}
Ajoutez une classe de base contenant les parties statiques de votre implémentation et une classe utilitaire pour créer vos instances dans:
class Product:
def __init__(self, version):
self.version = version
class ProductFactory(type):
@classmethod
def get_product_class(mcs, version):
# this will return an instance right away, due to the (version) in the end
return type.__new__(mcs, "Product_{}".format(version.replace(".","")), (Product,), {"function": funcs.get(version)})(version)
# if you want to return a class object to instantiate in your code omit the (version) in the end
En utilisant ceci:
p1 = ProductFactory.get_product_class("1.0")
p2 = ProductFactory.get_product_class("2.0")
print(p1.__class__.__name__) # Product_10
p1.function() # for version 1.0
print(p1.function) # <bound method func_10 of <__main__.Product_10 object at 0x0000000002A157F0>>
print(p2.__class__.__name__) # Product_20
p2.function() # for version 2.0
print(p2.function) # <bound method func_20 of <__main__.Product_20 object at 0x0000000002A15860>>
En général, ne pas. surcharge de méthode est déconseillé en Python. Si vous devez vous différencier au niveau de la classe, lisez la réponse de @ Loocid. Je ne vais pas répéter son excellent post.
Si vous voulez entrer dans un niveau de méthode parce que la différence est assez petite pour cela, essayez quelque chose comme ceci:
class Product:
def method(self):
if self.version == "1.0":
return self._methodv1()
Elif self.version == "2.0":
return self._methodv2()
else:
raise ValueError("No appropriate method for version {}".format(self.version))
def _methodv1(self):
# implementation
def _methodv2(self):
# implementation
Notez ici:
Ou:
class Product:
def method_old(self):
# transform arguments to v2 method:
return self.method()
def method(self):
# implementation
La première méthode me convient mieux, mais je voulais inclure la seconde pour la postérité. Si vous modifiez votre code dans 10 ans, celui-ci vous rendra plus heureux. Si vous modifiez le code en utilisant le code actuel demain, la première méthode vous rendra plus heureux.
Ou tu peux faire, dict.get
appeler une fonction et faire print
dans lambda
si rien n’est correct:
def func_1(self):
print('for version 1.0')
def func_2(self):
print('for version 2.0')
def function(self):
funcs = {"1.0": self.func_1,
"2.0": self.func_2}
funcs.get(self.version,lambda: print(f'function not support {self.version}'))()
Exemple:
class Product(object):
def __init__(self,version):
self.version = version
def func_1(self):
print('for version 1.0')
def func_2(self):
print('for version 2.0')
def function(self):
funcs = {"1.0": self.func_1,
"2.0": self.func_2}
funcs.get(self.version,lambda: print(f'function not support {self.version}'))()
Product('1.0').function()
Product('2.0').function()
Product('3.0').function()
Sortie:
for version 1.0
for version 2.0
function not support 3.0
Je ne suis pas un développeur python mais je ne peux pas m'empêcher de me demander pourquoi vous ne faites pas quelque chose comme ça:
class Product(object):
def __init__(self, version):
self.version = version
def function(self):
print('for version ' + self.version)
Vous pouvez utiliser des décorateurs pour cela
def v_decorate(func):
def func_wrapper(name):
return func(name)
return func_wrapper
Et
@v_decorate
def get_version(name):
return "for version {0} ".format(name)
Vous pouvez l'appeler
get_version(1.0)
'for version 1.0 '
get_version(2.0)
'for version 2.0 '