Domanda

In Java, per esempio, l'annotazione @Override non solo fornisce in fase di compilazione di una forzatura, ma rende per un'eccellente codice di auto-documentazione.

Sono solo in cerca di documentazione (anche se si tratta di un indicatore a qualche pedina come pylint, questo è un bonus). Posso aggiungere un commento o docstring da qualche parte, ma qual è il modo idiomatico per indicare un override in Python?

È stato utile?

Soluzione

UPDATE (2015/05/23): Sulla base di questo e FWC: s risposta che ha creato un pip installabile pacchetto https: // GitHub .com / mkorpela / ignora

A volte finisco qui guardando a questa domanda. Principalmente questo accade dopo (di nuovo) vedere lo stesso problema nella nostra base di codice: Qualcuno ha dimenticato un po ' "interfaccia" attuazione di classe, mentre la ridenominazione di un metodo nel "interfaccia" ..

Bene Python non è Java, ma Python ha il potere - ed esplicito è meglio di implicita -. E ci sono veri e propri casi concreti nel mondo reale in cui questa cosa mi avrebbe aiutato

Quindi, ecco un abbozzo di override decoratore. Questo controllerà che la classe dato come parametro ha il nome stesso metodo (o qualcosa) come metodo di essere decorato.

Se si può pensare ad una soluzione migliore si prega di postare qui!

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

Funziona come segue:

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


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

e se fate una versione difettosa alzerà un errore di asserzione durante il caricamento della classe:

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

>> AssertionError!!!!!!!

Altri suggerimenti

Ecco un'implementazione che non richiede specifica del nome 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

Se si desidera che questo solo a scopo di documentazione, è possibile definire il proprio decoratore di override:

def override(f):
    return f


class MyClass (BaseClass):

    @override
    def method(self):
        pass

Questa non è altro che eye-candy, a meno che non si crea esclusione (f) in modo tale che è in realtà controlla la presenza di una modifica locale.

Ma allora, questo è Python, perché scrivere è come se fosse Java?

Python non è Java. Non c'è ovviamente alcuna cosa in realtà come il controllo in fase di compilazione.

Credo che un commento nella docstring è abbondanza. Questo consente a qualsiasi utente del metodo di digitare help(obj.method) e vedere che il metodo è una sostituzione.

È inoltre possibile estendere in modo esplicito un'interfaccia con class Foo(Interface), che permetterà agli utenti di digitare help(Interface.method) per avere un'idea sulle funzionalità il metodo è destinato a fornire.

Come altri hanno detto a differenza di Java non c'è tag @Overide comunque sopra potete creare i propri utilizzando decoratori tuttavia Io suggerirei di usare il getattrib () metodo globale invece di utilizzare il dict interna in modo da ottenere qualcosa di simile al seguente:

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

Se si voleva si poteva prendere getattr () nel proprio tentativo di cattura aumentare il proprio errore, ma penso che il metodo getattr è meglio in questo caso.

Anche questa cattura tutte le voci legate a una classe compresi i metodi e le vairables di classe

grande risposta , ecco una versione con

controlli più precisi, la denominazione, e gli oggetti di errore sollevate

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

Ecco come si presenta nella pratica:

NotImplementedError " non implementato in classe base "

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

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

Risultati in errore NotImplementedError più descrittivo

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

stack completo

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 implementato previsto "

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

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

Risultati in errore NotImplementedError più descrittivo

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

stack completo

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 cosa grandiosa di @mkorpela risposta è la verifica avviene durante una certa fase di inizializzazione. Il controllo non ha bisogno di essere "run". Con riferimento agli esempi precedenti, class B viene mai inizializzato (B()) tuttavia il NotImplementedError ancora aumentare. Questo significa che gli errori overrides vengono catturati prima.

In base a grande risposta di @ mkorpela, ho scritto un pacchetto simile ( ipromise PyPI github ) che fa molti più controlli:

Si supponga che un eredita da B e C. E B eredita da C. ipromise verifica che

  • Se A.F sovrascrive B.f, B.f deve esistere, e A deve ereditare da B. (Questo è il controllo dal pacchetto override).

  • Non è il modello Af dichiara che la priorità Bf, che poi dichiara che prevale Cf A deve dire che prevale dal Cf dal B potrebbe decidere di fermare l'override di questo metodo, e che non deve comportare negli aggiornamenti a valle.

  • Non è il modello A.F dichiara che la priorità C.f, ma B.f non dichiara la sua sostituzione.

  • Non è il modello A.F dichiara che la priorità C.f, ma B.f dichiara che prevale da qualche D.F.

Inoltre ha varie caratteristiche per la marcatura e controllo attuare un metodo astratto.

sente è più semplice e operante sotto Jython con le classi 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()
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top