35 votes

Même le nom des fonctions dans la même classe, élégante façon de déterminer les appeler?

Je suis en train de faire des produits de contrôle de version dans les scripts Python pour une raison bien précise, mais je ne pouvais pas comprendre comment le faire d'une manière élégante - s'il vous plaît aider.

Actuellement, je suis en train de faire quelque chose comme ci-dessous. Cependant, les scripts sont difficiles à maintenir, lorsque la version du contenu 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}')

Donc, je veux faire quelque chose comme ci-dessous afin de séparer les fonctions avec le 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 de réaliser ceci:

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 réussi à trouver comment... semble comme un décorateur ne peut pas faire ce genre d'opération ou je ne comprends juste pas comment.

Est-il un moyen élégant pour ce faire?

33voto

Bi Rico Points 9851

L'héritage est probablement la meilleure façon de le faire, mais puisque vous avez demandé spécifiquement sur les décorateurs, je voulais montrer que vous pouvez faire cela en utilisant les décorateurs.

Vous aurez besoin d'utiliser un dictionnaire pour stocker vos fonctions par version, regardez en haut de la version à utiliser lors 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()

9voto

Loocid Points 2101

Pourriez-vous mettre votre Product classe en deux modules, v1 et v2, puis de l'importer sous certaines conditions?

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

Ensuite 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()

7voto

shmee Points 1201

Comme autre option, vous pourrait aller pour certains factory pour créer votre classe.

Créez votre versionnées fonctions (note de l' self paramètre). Cela peut être fait dans un autre module. Aussi, ajouter un peu de collecte à récupérer la fonction basée sur le 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}

Ajouter une classe de base qui contient les éléments statiques de la mise en place et une classe utilitaire pour créer votre instances:

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

6voto

Gloweye Points 38

En général, n'en ont pas. La surcharge de méthode est déconseillée en Python. Si tu dois faire la différence sur le niveau de la classe, lire @Loocid de réponse. Je ne vais pas répéter son excellent post.

Si vous voulez sur un niveau de la méthode, car la différence est assez petit pour que, d'essayer 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:

  1. L'utilisation de méthodes en commençant par un trait de soulignement pour indiquer ceux qui sont la mise en œuvre.
  2. En gardant les méthodes agréable et bien rangé sans la contamination entre les versions
  3. Générer une erreur inattendue versions (accident et dur).
  4. Dans mon pas si humble avis, ce sera beaucoup plus clair pour les autres la lecture de votre post que d'utiliser des décorateurs.

Ou:

class Product:

    def method_old(self):
        # transform arguments to v2 method:
        return self.method()

    def method(self):
        # implementation
  1. Dans le cas où vous voulez totalement déprécier précédente utilisation, et que vous voulez déposer la version 1.0 de soutien dans l'avenir.
  2. Assurez-vous de donner à la dépréciation des avertissements, afin de ne pas surprendre les utilisateurs d'une bibliothèque avec de brusques changements.
  3. Sans doute la meilleure solution si personne d'autre n'utilise votre code.

Je reçois l'ambiance de ma première méthode serait plus adaptée à votre problème à portée de main, mais je voulais inclure le deuxième pour la postérité. Si vous modifiez votre code de 10 ans à partir de maintenant, que l'on va vous rendre plus heureux. Si vous modifiez le code en utilisant le code actuel, demain, la première méthode va vous rendre plus heureux.

2voto

Jack Points 580

Je ne suis pas un développeur python mais je ne peux pas aider mais se demander pourquoi vous n'avez pas tout simplement faire quelque chose comme ceci:

class Product(object):
    def __init__(self, version):
        self.version = version
    def function(self):
        print('for version ' + self.version)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X