En Python, ¿cómo indico que estoy anulando un método?
-
19-09-2019 - |
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?
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()