2009-07-22 12 views
98

En Java, por ejemplo, la anotación @Override no solo proporciona la verificación en tiempo de compilación de una anulación sino que proporciona un excelente código de auto-documentación. Solo estoy buscando documentación (aunque si es un indicador para algún inspector como la pildora, eso es una ventaja). Puedo agregar un comentario o un docstring en alguna parte, pero ¿cuál es la manera idiomática de indicar una anulación en Python?En Python, ¿cómo señalo que estoy anulando un método?

+10

En otras palabras, que no siempre indican que Estás anulando un método? ¿Dejar que el lector lo descubra? – Bluu

+2

Sí, sé que parece una situación propensa a errores procedente de un lenguaje compilado, pero solo tiene que aceptarlo. En la práctica, no he encontrado que sea un gran problema (Ruby en mi caso, no Python, pero la misma idea) –

+0

Claro, hecho. Tanto la respuesta de Triptych como las respuestas de mkorpela son simples, me gusta, pero el espíritu explícito sobre implícito de este último, y el error de prevención inteligible, gana. – Bluu

Respuesta

140

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

De vez en cuando llegué aquí mirando a esta pregunta. Principalmente esto sucede después de (otra vez) ver el mismo error en nuestra base de código: Alguien ha olvidado alguna clase de implementación de "interfaz" al cambiar el nombre de un método en la "interfaz".

Bueno Python no es Java pero Python tiene el poder -y explícito es mejor que implícito- y hay casos reales reales en el mundo real donde esto me habría ayudado.

Así que aquí hay un boceto del decodificador de anulaciones. Esto verificará que la clase dada como parámetro tenga el mismo nombre de método (o algo) que el método que se está decorando.

Si puede pensar en una solución mejor, ¡por favor publíquela 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!!!!!!! 
+11

Impresionante. Esto captó un error ortográfico la primera vez que lo intenté. Prestigio. –

+1

Entonces, estoy usando python 2.7 y si mi clase amplía muchas otras clases y, a diferencia de la interfaz, no quiero codificar el nombre de clase exacto que contiene la función de interfaz, entonces esto puede funcionar en general si ¿Heredé de más de una clase o el orden de resolución del método lo romperá? –

+0

¿Cómo escribirías una afirmación que verifica si el número de argumentos es el mismo en la clase base/interfaz y en la clase hija? – Skarab

4

Hasta donde yo sé, no hay una forma especial de indicar una anulación en Python. Simplemente defina el método e incluya una docstring, como siempre.

8

Python no es Java. Por supuesto, no existe tal cosa como la verificación en tiempo de compilación.

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

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

+40

El verdadero objetivo de '@ Override' en Java no es documentar, sino cometer un error cuando intentas anular un método, pero terminaste definiendo uno nuevo (por ejemplo, porque escribiste mal un nombre; en Java, puede también sucede porque usaste la firma incorrecta, pero esto no es un problema en Python, pero aún así es un error ortográfico). –

+2

@ Pavel Minaev: Es cierto, pero aún así es conveniente tenerlo para la documentación, especialmente si está usando un editor de IDE/texto que no tiene indicadores automáticos para anulaciones (el JDT de Eclipse los muestra prolijamente junto con los números de línea, por ejemplo) . –

+2

@PavelMinaev Incorrecto. Uno de los puntos principales de '@ Override' es la documentación además de la verificación del tiempo de compilación. – siamii

8

Si desea que esta para fines de documentación única, puede definir su propia anulación decorador:

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 si hay una anulación.

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

+2

Se podría agregar una validación real mediante inspección al decorador 'anular'. –

+25

* Pero entonces, este es Python, ¿por qué escribirlo como si fuera Java? * ¿Porque algunas ideas en Java son buenas y vale la pena extenderlas a otros lenguajes? –

+4

Porque cuando cambia el nombre de un método en una superclase, sería bueno saber que algunos niveles de la subclase 2 están anulándolo. Claro, es fácil de verificar, pero un poco de ayuda del analizador de lenguaje no dolería. – Abgan

21

Aquí es una implementación que no lo hace requieren especificación del nombre de la clase de interfaz.

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

Un poco mágico pero hace que el uso típico sea mucho más fácil. ¿Puedes incluir ejemplos de uso? – Bluu

+0

¿Cuáles son los costos promedio y en el peor de los casos de usar este decorador, quizás expresados ​​como una comparación con un decorador incorporado como @classmethod o @property? – larham1

+2

@ larham1 Este decorador se ejecuta una vez, cuando se analiza la definición de clase, no en cada llamada. Por lo tanto, su costo de ejecución es irrelevante, en comparación con el tiempo de ejecución del programa. – Abgan

2

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 así como lo siguiente:

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

Si quería se podía tomar 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

0

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() 
Cuestiones relacionadas