In Python, come faccio indico sto override un metodo?
-
19-09-2019 - |
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?
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()