Question

En Java, par exemple, l'annotation @Override non seulement fournit la compilation d'une vérification de remplacement, mais fait pour un excellent code auto-documenté.

Je suis à la recherche de la documentation (bien que si elle est un indicateur à certains vérificateur comme pylint, c'est un bonus). Je peux ajouter un commentaire ou docstring quelque part, mais quel est le moyen idiomatiques pour indiquer un remplacement en Python?

Était-ce utile?

La solution

Mise à jour (23/05/2015): Sur cette base et FWC: de la réponse que je crée un paquet installable pépin https: // github .com / mkorpela / l'emporte sur

De temps en temps, je finis par regarder ici à cette question. Principalement cela se produit après (nouveau) voir le même bug dans notre base de code: Quelqu'un a oublié une classe implémentant « interface » en renommant une méthode dans la « interface » ..

Eh bien Python est pas Java mais Python a le pouvoir - et explicite est mieux que implicite -. Et il y a des cas concrets réels dans le monde réel où cette chose me aurait aidé

Voici donc une esquisse de décorateur overrides. Cela vérifiera que la classe donnée comme paramètre a la même méthode (ou quelque chose) nom que la méthode étant décorée.

Si vous pouvez penser à une meilleure solution s'il vous plaît poster ici!

def overrides(interface_class):
    def overrider(method):
        assert(method.__name__ in dir(interface_class))
        return method
    return overrider

Il fonctionne comme suit:

class MySuperInterface(object):
    def my_method(self):
        print 'hello world!'


class ConcreteImplementer(MySuperInterface):
    @overrides(MySuperInterface)
    def my_method(self):
        print 'hello kitty!'

et si vous faites une version défectueuse, il déclenche une erreur d'assertion lors du chargement de la classe:

class ConcreteFaultyImplementer(MySuperInterface):
    @overrides(MySuperInterface)
    def your_method(self):
        print 'bye bye!'

>> AssertionError!!!!!!!

Autres conseils

Voici une implémentation qui ne nécessite pas la spécification du nom interface_class.

import inspect
import re

def overrides(method):
    # actually can't do this because a method is really just a function while inside a class def'n  
    #assert(inspect.ismethod(method))

    stack = inspect.stack()
    base_classes = re.search(r'class.+\((.+)\)\s*\:', stack[2][4][0]).group(1)

    # handle multiple inheritance
    base_classes = [s.strip() for s in base_classes.split(',')]
    if not base_classes:
        raise ValueError('overrides decorator: unable to determine base class') 

    # stack[0]=overrides, stack[1]=inside class def'n, stack[2]=outside class def'n
    derived_class_locals = stack[2][0].f_locals

    # replace each class name in base_classes with the actual class type
    for i, base_class in enumerate(base_classes):

        if '.' not in base_class:
            base_classes[i] = derived_class_locals[base_class]

        else:
            components = base_class.split('.')

            # obj is either a module or a class
            obj = derived_class_locals[components[0]]

            for c in components[1:]:
                assert(inspect.ismodule(obj) or inspect.isclass(obj))
                obj = getattr(obj, c)

            base_classes[i] = obj


    assert( any( hasattr(cls, method.__name__) for cls in base_classes ) )
    return method

Si vous voulez que ce à des fins de documentation uniquement, vous pouvez définir votre propre décorateur de priorité:

def override(f):
    return f


class MyClass (BaseClass):

    @override
    def method(self):
        pass

Ceci est vraiment rien, mais eye-candy, sauf si vous créez override (f) de telle manière est vérifie en fait un remplacement.

Mais alors, c'est Python, pourquoi écrire comme il était Java?

Python est pas Java. Il y a bien sûr pas une telle chose vraiment que la vérification de la compilation.

Je pense un commentaire dans le docstring est beaucoup. Cela permet à tout utilisateur de votre méthode pour saisir help(obj.method) et voir que la méthode est une dérogation.

Vous pouvez également étendre explicitement une interface avec class Foo(Interface), qui permettra aux utilisateurs de taper help(Interface.method) pour avoir une idée sur la fonctionnalité de votre méthode est destinée à fournir.

Comme d'autres l'ont dit à la différence de Java il n'y a pas balise @Overide cependant ci-dessus, vous pouvez créer vos propres à l'aide de décorateurs mais je suggère d'utiliser la getAttrib () méthode globale au lieu d'utiliser le dict interne de sorte que vous obtenez quelque chose comme ce qui suit:

def Override(superClass):
    def method(func)
        getattr(superClass,method.__name__)
    return method

Si vous vous vouliez pourrait prendre getattr () dans votre propre prise try augmenter votre propre erreur, mais je pense que la méthode getattr vaut mieux dans ce cas.

Aussi ce attrape tous les éléments liés à une classe, y compris les méthodes de classe et vairables

Improviser sur @mkorpela grande réponse , voici une version avec

contrôles plus précis, en nommant, et des objets élevés d'erreur

def overrides(interface_class):
    """
    Function override annotation.
    Corollary to @abc.abstractmethod where the override is not of an
    abstractmethod.
    Modified from answer https://stackoverflow.com/a/8313042/471376
    """
    def confirm_override(method):
        if method.__name__ not in dir(interface_class):
            raise NotImplementedError('function "%s" is an @override but that'
                                      ' function is not implemented in base'
                                      ' class %s'
                                      % (method.__name__,
                                         interface_class)
                                      )

        def func():
            pass

        attr = getattr(interface_class, method.__name__)
        if type(attr) is not type(func):
            raise NotImplementedError('function "%s" is an @override'
                                      ' but that is implemented as type %s'
                                      ' in base class %s, expected implemented'
                                      ' type %s'
                                      % (method.__name__,
                                         type(attr),
                                         interface_class,
                                         type(func))
                                      )
        return method
    return confirm_override

Voici à quoi il ressemble dans la pratique:

NotImplementedError " pas mis en œuvre dans la classe de base "

class A(object):
    # ERROR: `a` is not a implemented!
    pass

class B(A):
    @overrides(A)
    def a(self):
        pass

résultats en erreur NotImplementedError plus descriptif

function "a" is an @override but that function is not implemented in base class <class '__main__.A'>

pile complète

Traceback (most recent call last):
  …
  File "C:/Users/user1/project.py", line 135, in <module>
    class B(A):
  File "C:/Users/user1/project.py", line 136, in B
    @overrides(A)
  File "C:/Users/user1/project.py", line 110, in confirm_override
    interface_class)
NotImplementedError: function "a" is an @override but that function is not implemented in base class <class '__main__.A'>

NotImplementedError " de type mis en œuvre attendue "

class A(object):
    # ERROR: `a` is not a function!
    a = ''

class B(A):
    @overrides(A)
    def a(self):
        pass

résultats en erreur NotImplementedError plus descriptif

function "a" is an @override but that is implemented as type <class 'str'> in base class <class '__main__.A'>, expected implemented type <class 'function'>

pile complète

Traceback (most recent call last):
  …
  File "C:/Users/user1/project.py", line 135, in <module>
    class B(A):
  File "C:/Users/user1/project.py", line 136, in B
    @overrides(A)
  File "C:/Users/user1/project.py", line 125, in confirm_override
    type(func))
NotImplementedError: function "a" is an @override but that is implemented as type <class 'str'> in base class <class '__main__.A'>, expected implemented type <class 'function'>


La grande chose au sujet @mkorpela réponse est la vérification se produit pendant une phase d'initialisation. La vérification n'a pas besoin d'être « run ». En se référant aux exemples précédents, class B est jamais initialisé (B()) mais le NotImplementedError soulèvera encore. Cela signifie que les erreurs de overrides sont pris plus tôt.

Basé sur une grande réponse @ mkorpela, j'ai écrit un paquet similaire ( ipromise pypi github ) qui fait beaucoup plus de contrôles:

Supposons que A hérite de B et C et B hérite de C. ipromise vérifie que

  • Si A.f remplace B.f, B.f doit exister, et A doit hériter de B. (Ceci est la vérification du paquetage overrides).

  • Vous n'avez pas le modèle Af déclare qu'elle l'emporte sur Bf, qui déclare alors qu'elle l'emporte sur A Cf devrait dire qu'il remplace de B puisque Cf peut décider d'arrêter redéfinissant cette méthode, et cela ne devrait pas entraîner dans les mises à jour en aval.

  • Vous n'avez pas le modèle A.f déclare qu'elle l'emporte sur C.f, mais B.f ne déclare pas son remplacement.

  • Vous n'avez pas le modèle A.f déclare qu'elle l'emporte sur C.f, mais B.f déclare qu'elle l'emporte de certains D.F.

Il a également diverses fonctionnalités de marquage et de vérification de la mise en œuvre d'une méthode abstraite.

Entendre est plus simple et travailler sous Jython avec des classes Java:

class MyClass(SomeJavaClass):
     def __init__(self):
         setattr(self, "name_of_method_to_override", __method_override__)

     def __method_override__(self, some_args):
         some_thing_to_do()
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top