Ein Nachtrag zu @nailxx ‚s Antwort:
Sie __test__ = False
in der übergeordneten Klasse setzen konnten und dann eine Metaklasse verwenden (siehe This question mit einigen brillanten Erklärungen) es auf True zurück zu setzen, wenn Subklassen.
(Schließlich fand ich eine Entschuldigung, eine Metaklasse zu verwenden!)
Obwohl __test__
ein Doppelstrich Attribut ist, müssen wir es explizit auf True
gesetzt, da nicht einstellen würde es dazu führen, Python nur zum Nachschlagen das Attribut weiter auf MRO und bewerten Sie es zu False
.
Daher müssen wir bei der Klasseninstanziierung prüfen, ob eine der Elternklassen __test__ = False
hat. Wenn dies der Fall ist und die aktuelle Klassendefinition __test__
nicht selbst gesetzt hat, fügen wir '__test__': True
zu den Attributen dict hinzu.
Der resultierende Code sieht wie folgt aus:
class TestWhenSubclassedMeta(type):
"""Metaclass that sets `__test__` back to `True` when subclassed.
Usage:
>>> class GenericTestCase(TestCase, metaclass=TestWhenSubclassed):
... __test__ = False
...
... def test_something(self):
... self.fail("This test is executed in a subclass, only.")
...
...
>>> class SpecificTestCase(GenericTestCase):
... pass
"""
def __new__(mcs, name, bases, attrs):
ATTR_NAME = '__test__'
VALUE_TO_RESET = False
RESET_VALUE = True
values = [getattr(base, ATTR_NAME) for base in bases
if hasattr(base, ATTR_NAME)]
# only reset if the first attribute is `VALUE_TO_RESET`
try:
first_value = values[0]
except IndexError:
pass
else:
if first_value == VALUE_TO_RESET and ATTR_NAME not in attrs:
attrs[ATTR_NAME] = RESET_VALUE
return super().__new__(mcs, name, bases, attrs)
Man könnte dies zu einem gewissen mehr impliziten Verhalten erweitern wie „wenn der Name mit Abstract
beginnt, setzte __test__ = False
automatisch“, aber ich für mich die explizite Zuordnung halten würde zur Klarheit.
Lassen Sie mich einfach Unittests einfügen, das Verhalten zu demonstrieren - und als Erinnerung daran, dass jeder die zwei Minuten dauern soll, ihren Code zu testen, nachdem eine Funktion einzuführen.
from unittest import TestCase
from .base import TestWhenSubclassedMeta
class SubclassesTestCase(TestCase):
def test_subclass_resetted(self):
class Base(metaclass=TestWhenSubclassedMeta):
__test__ = False
class C(Base):
pass
self.assertTrue(C.__test__)
self.assertIn('__test__', C.__dict__)
def test_subclass_not_resetted(self):
class Base(metaclass=TestWhenSubclassedMeta):
__test__ = True
class C(Base):
pass
self.assertTrue(C.__test__)
self.assertNotIn('__test__', C.__dict__)
def test_subclass_attr_not_set(self):
class Base(metaclass=TestWhenSubclassedMeta):
pass
class C(Base):
pass
with self.assertRaises(AttributeError):
getattr(C, '__test__')
Der '__test__ = false' Trick im Falle einer übergeordneten Klasse nicht sehr nützlich ist, würde diese Kinder Klassen zwingen explizit angeben' __test__ = true' oder es würde ignoriert werden, das ist ein gefährlicher Trick zu benutzen. – Guibod