Pregunta

En Java, por ejemplo, el @Override La anotación no solo proporciona verificación en tiempo de compilación de una anulación, sino que también constituye un excelente código autodocumentado.

Solo estoy buscando documentación (aunque si es un indicador para algún verificador como pylint, es una ventaja).Puedo agregar un comentario o una cadena de documentación en algún lugar, pero ¿cuál es la forma idiomática de indicar una anulación en Python?

¿Fue útil?

Solución

ACTUALIZACIÓN (05/23/2015): Basado en esto y fwc: s respuesta que creó un pip instalable paquete https: // github .com / mkorpela / anula

De vez en cuando llegué aquí mirando a esta pregunta. Principalmente esto ocurre después (una vez más) viendo el mismo error en nuestra base de código: Alguien ha olvidado un poco de "interfaz" la implementación de la clase, mientras que el cambio de nombre de un método en el "interfaz" ..

Bien Python no es Java, pero Python tiene el poder - y explícito es mejor que implícito -. Y hay casos concretos reales en el mundo real, donde esta cosa me habría ayudado

Así que aquí es un esbozo de las anulaciones decorador. Esto comprobará que la clase dada como parámetro tiene el nombre mismo método (o algo así) como el método de ser decorado.

Si usted puede pensar en una solución mejor publicarlo aquí!

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

Funciona de la siguiente manera:

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


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

y si lo hace una versión defectuosa se generará un error afirmación durante la carga de clases:

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

>> AssertionError!!!!!!!

Otros consejos

Esta es una implementación que no requiere la especificación del nombre 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 desea que esta para fines de documentación única, puede definir su propio decorador de anulación:

def override(f):
    return f


class MyClass (BaseClass):

    @override
    def method(self):
        pass

Esto es realmente nada más que los ojos dulces, a no ser que se crea anulación (f) de tal manera que es en realidad comprueba una anulación.

Pero entonces, esto es Python, ¿por qué escribir como si fuera de Java?

Python no es Java. Hay, por supuesto, no hay tal cosa realmente como comprobar el tiempo de compilación.

Creo que un comentario en la cadena de documentación es suficiente. Esto permite a cualquier usuario de su método para escribir help(obj.method) y ver que el método es una anulación.

También puede extender de manera explícita una interfaz con class Foo(Interface), lo que permitirá a los usuarios escribir help(Interface.method) para tener una idea acerca de la funcionalidad de su método está destinado a proporcionar.

Como otros han dicho a diferencia de Java no hay etiqueta @Overide sin embargo por encima de usted puede crear sus propios utilizando decoradores sin embargo se recomienda usar el getattrib () método global en lugar de utilizar el dict interna para que pueda obtener algo como lo siguiente:

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

Si quieres que se pueda enganchar getattr () en su propio intento de captura elevar su propio error, pero creo que el método getattr es mejor en este caso.

También este atrapa todos los elementos unidos a una clase que incluye métodos de clase y vairables

Improvisando en @mkorpela gran respuesta, aquí hay una versión con

comprobaciones, nombres y objetos de error generados más precisos

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


Así es como se ve en la práctica:

NotImplementedError "no implementado en la clase base"

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

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

resulta en más descriptivo NotImplementedError error

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

completa pila

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 "tipo implementado esperado"

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

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

resulta en más descriptivo NotImplementedError error

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

completa pila

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




Lo mejor de la respuesta de @mkorpela es que la verificación se realiza durante alguna fase de inicialización.No es necesario "ejecutar" la verificación.Refiriéndose a los ejemplos anteriores, class B nunca se inicializa (B()) sin embargo, el NotImplementedError todavía aumentará.Esto significa overrides Los errores se detectan antes.

Sobre la base de gran respuesta de @ mkorpela, he escrito un paquete similar ( iPromise PyPI github ) que hace muchos más comprobaciones:

Supongamos que A hereda de B y C. Y B hereda de C. iPromise cheques que

  • Si A.f anula B.f, B.f debe existir, y A debe heredar de B. (Esta es la verificación del paquete anulaciones).

  • Usted no tiene el patrón Af declara que anula Bf, que luego declara que anula CF debe decir que anula de Cf ya que B podría decidir dejar de anular este método, y que no debe resultar en las actualizaciones de aguas abajo.

  • Usted no tiene el patrón A.f declara que anula C.f, pero B.f no declara su anulación.

  • Usted no tiene el patrón A.f declara que anula C.f, pero B.f declara que anula de algún D. F.

También cuenta con varias características para el marcado y comprobación de la aplicación de un método abstracto.

se escucha es más simple y funciona bajo Jython con clases 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()
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top