In Python, wie kann ich zeigen, ich bin überschreiben eine Methode?
-
19-09-2019 - |
Frage
In Java, zum Beispiel der @Override
Annotation bietet nicht nur kompilieren Zeit eine Vorrangprüfung, sondern für hervorragenden Selbst dokumentiert Code macht.
Ich bin nur für die Dokumentation (obwohl, wenn es bis zu einem gewissen checker wie Pylint ein Indikator ist, dass ein Bonus ist). Ich kann einen Kommentar hinzufügen oder irgendwo Docstring, aber was ist der idiomatische Weg eine Überschreibung in Python, um anzuzeigen?
Lösung
UPDATE (2015.05.23): Auf dieser Basis und fwc: s Antwort habe ich ein pip installierbaren Paket https: // Github .com / mkorpela / überschreibt
Von Zeit zu Zeit habe ich am Ende hier oben auf diese Frage suchen. Vor allem die gleichen Fehler in unserer Code-Basis zu sehen, dies geschieht nach (wieder): Jemand eine „Schnittstelle“ implementierende Klasse vergessen hat, während eine Methode in der „Schnittstelle“ ..
UmbenennenNun Python ist nicht Java aber Python hat die Macht - und explizite ist besser als implizit -. Und es gibt reale konkrete Fälle in der realen Welt, wo dieses Ding mir geholfen hätte
So, hier ist eine Skizze von Überschreibungen Dekorateur. Diese prüft, ob die Klasse als Parameter angegeben hat die gleiche Methode (oder etwas) Namen wie das Verfahren eingerichtet werden.
Wenn Sie eine bessere Lösung denken kann es bitte hier posten!
def overrides(interface_class):
def overrider(method):
assert(method.__name__ in dir(interface_class))
return method
return overrider
Es funktioniert wie folgt:
class MySuperInterface(object):
def my_method(self):
print 'hello world!'
class ConcreteImplementer(MySuperInterface):
@overrides(MySuperInterface)
def my_method(self):
print 'hello kitty!'
und wenn Sie eine fehlerhafte Version tun wird es eine Assertion Fehler beim Laden von Klassen erhöhen:
class ConcreteFaultyImplementer(MySuperInterface):
@overrides(MySuperInterface)
def your_method(self):
print 'bye bye!'
>> AssertionError!!!!!!!
Andere Tipps
Hier ist eine Implementierung, die keine Spezifikation des interface_class Namen erforderlich macht.
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
Wenn Sie diese Dokumentation zu wollen dient nur, können Sie Ihre eigene Überschreibung Dekorateur definieren:
def override(f):
return f
class MyClass (BaseClass):
@override
def method(self):
pass
Das ist wirklich nichts anderes als Augenschmaus, es sei denn, Sie überschreiben (f) in einer solchen Art und Weise erstellen, die tatsächlich überprüft für eine Überschreibung ist.
Aber dann, dieser Python ist, warum es schreiben, wie es Java war?
Python ist nicht Java. Es gibt natürlich so etwas wirklich nicht als Übersetzungszeit zu überprüfen.
Ich glaube, ein Kommentar in der docstring viel ist. Dies ermöglicht es jedem Benutzer Ihrer Methode help(obj.method)
zu geben und sehen, dass das Verfahren eine Überschreibung ist.
Sie können auch explizit eine Schnittstelle mit class Foo(Interface)
erweitern, die Benutzern erlaubt, help(Interface.method)
zu geben eine Vorstellung über die Funktionalität Ihrer Methode zur Verfügung zu stellen ist, bekommen soll.
Wie andere haben im Gegensatz zu Java sagt, dass es nicht @Overide Tag ist jedoch über Sie Ihre eigenen mit Dekorateure erstellen können jedoch würde ich vorschlagen, die getattrib () verwenden globale Methode anstelle der Verwendung des internen dict, so dass Sie so etwas wie die folgenden bekommen:
def Override(superClass):
def method(func)
getattr(superClass,method.__name__)
return method
Wenn Sie Sie wollte konnte getattr () in Ihrem eigenen try catch heben Sie Ihre eigenen Fehler fangen, aber ich denke getattr Methode in diesem Fall besser ist.
Auch fangen diese alle zu einer Klasse gebunden Elemente einschließlich Klassenmethoden und vairables
Improvisieren auf @mkorpela große Antwort , hier ist eine Version mit
präzisere Kontrollen, Benennen und erhöhte Fehlerobjekte
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
Hier ist, wie es in der Praxis aussieht:
NotImplementedError
" nicht in Basisklasse implementiert "
class A(object):
# ERROR: `a` is not a implemented!
pass
class B(A):
@overrides(A)
def a(self):
pass
Ergebnisse in aussagekräftigeren NotImplementedError
Fehler
function "a" is an @override but that function is not implemented in base class <class '__main__.A'>
Fullstack
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
" erwartet umgesetzt Typ "
class A(object):
# ERROR: `a` is not a function!
a = ''
class B(A):
@overrides(A)
def a(self):
pass
Ergebnisse in aussagekräftigeren NotImplementedError
Fehler
function "a" is an @override but that is implemented as type <class 'str'> in base class <class '__main__.A'>, expected implemented type <class 'function'>
Fullstack
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'>
Die große Sache über @mkorpela Antwort ist die Überprüfung während einiger Initialisierungsphase passiert. Die Prüfung muss nicht „run“ sein. Unter Bezugnahme auf die vorhergehenden Beispiele wird class B
nie (B()
) initialisiert noch die NotImplementedError
noch erhöhen. Das bedeutet, overrides
Fehler früher aufgefangen werden.
Basierend auf @ mkorpela die große Antwort, ich habe ein ähnliches Paket geschrieben ( iPromise pypi github ), die viele mehr Kontrollen tut:
Nehmen wir an A erbt von B und C und B erbt von C. iPromise prüft, dass
-
Wenn A.f B.f überschreibt, muss B.f vorhanden sind, und A muss von B. erben (Dies ist die Überprüfung der Überschreibungen Paket).
-
Sie haben nicht das Muster Af erklärt, dass sie überschreibt Bf, die dann erklärt, dass es Cf A hat Vorrang sollte sagen, dass es von Cf überschreibt da B Überschreiben dieser Methode zu stoppen könnte sich entscheiden, und das sollte nicht dazu führen, in Downstream-Updates.
-
Sie haben nicht das Muster A.f erklärt, dass es überschreibt C.F, aber B.f erklärt nicht seine außer Kraft setzen.
-
Sie haben nicht das Muster A.f erklärt, dass es überschreibt C.F, aber B.f erklärt, dass sie von einigen d.f. überschreibt
Es hat auch verschiedene Funktionen zur Markierung und Überprüfung einer abstrakte Methode zu implementieren.
Hear ist am einfachsten und Arbeiten unter Jython mit Java-Klassen:
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()