Zunächst einmal: Sie verwenden das abc
Modul falsch (see the docs). Ihre Klasse A
sollte abc.ABCMeta
als Meta-Klasse haben. Wenn Sie also bereits eine Meta-Klasse verwenden, können Sie diese zu Ihrem Vorteil erweitern.
Eine Meta-Klasse, die von abc.ABCMeta
erbt abstractmethod
Arbeit zu machen und schmückt do_thing
:
from abc import ABCMeta, abstractmethod
class DecoratingMeta(ABCMeta):
def __new__(cls, *args):
new_class = super(DecoratingMeta, cls).__new__(cls, *args)
# decorating do_thing manually
new_class.do_thing = new_class.do_thing_decorator(new_class.do_thing)
return new_class
Jetzt ist Ihre abstrakte Basisklasse mit einem Dekorateur Standardprüfung, die nichts tut:
# class Abstract(metaclass=ABCMeta): in python3
class Abstract(object):
__metaclass__ = DecoratingMeta # remove this line in python3
@abstractmethod
def do_thing(self, input):
pass
@classmethod
def do_thing_decorator(cls, function):
return function # default decorator that does nothing
Beachten Sie, dass do_thing_decorator
muss in diesem Fall eine Klassenmethode sein. Für Metaklassen, die in python3
und python2
funktionieren, siehe six.
Ihre Checker-Klasse, die eine bestimmte checker nur implementiert, sondern ist immer noch abstrakt:
class Checker(Abstract):
@classmethod
def do_thing_decorator(cls, function):
def do_checked_thing(self, input):
check = function(self, input) # NOT self.do_thing(input) else recursion error
if check != 1:
raise ValueError("Check failed")
return check
return do_checked_thing
Beachten Sie, dass die Linie, die Sie check = do_thing_func(input)
schrieb in einem Rekursionsfehlers führen würde.
Und Ihre konkrete Klasse mit einer Beispielimplementierung von do_thing
:
class Concrete(Checker):
def do_thing(self, input):
return input # sample implementation
Sie können überprüfen, ob do_thing(1)
gelingt und do_thing(2)
nicht
c = Concrete()
c.do_thing(1)
try:
c.do_thing(2)
except ValueError:
print("check failed")
Der Nachteil bei diesem Ansatz ist, dass Sie nicht machen können die do_thing_decorator
Zusammenfassung.
So war das schon viel Text, aber wenn Sie überhaupt keine Meta-Klassen nicht verwenden wollen, ist es eine viel einfachere Art und Weise:
eine Klasse schreiben, die die Prüfung in der do_thing
Methode führt durch mit zwei „abstrakt“ Methoden:
class Abstract(object):
def do_thing_func(self, input):
raise NotImplementedError()
def check_do_thing(self, result):
raise NotImplementedError()
# don't override this method
def do_thing(self, input):
result = self.do_thing_func(input)
self.check_do_thing(result) # may raise
return result # if it does not raise return the result
Beachten Sie, dass do_thing_func
und check_do_thing
sind nicht wirklich abstrakt und dass man noch Instanciate Objekte vom Typ Abstract
. Wenn Sie sie benötigen, um abstrakt zu sein, verwenden Sie eine Standard abc.ABCMeta
Meta-Klasse hier.
nun eine Kontrolleur Klasse erstellen, die check_do_thing
class Checker(Abstract):
def check_do_thing(self, result):
if result != 1:
raise ValueError("check failed")
Diese implementiert wird wesentlich einfacher, weil wir nicht hier einen Dekorateur brauchen.
Und schließlich die konkrete Klasse, die do_thing_func
class Concrete(Checker):
def do_thing_func(self, input):
return input # sample implementation
Hinweis implementiert, dass Concrete
jetzt do_thing_func
implementieren muss, aber wenn Sie die Klasse verwenden Sie haben do_thing
zu nennen.
Der Nachteil hier ist, dass Sie immer noch do_thing
überschreiben und damit die Prüfung brechen können.
Haben Sie sich die '__new __()' Methode angesehen? –
@ IgnacioVazquez-Abrams keine unangemessene Lösung. Ich habe auf etwas auf der Methodenebene etwas intuitiver gehofft, werde aber wahrscheinlich darauf zurückgreifen, wenn ich nichts Besseres finde. –