Jeden dodatek do odpowiedzi @nailxx „s:
Można ustawić __test__ = False
w klasie macierzystej, a następnie użyć metaklasa (patrz This question z niektórych genialnych wyjaśnień), aby ustawić go na wartość True, gdy instacji.
(Wreszcie znalazłem pretekst do korzystania z metaklasa!)
Chociaż __test__
jest dwukrotnie atrybut podkreślenia, musimy jawnie ustawić go True
, ponieważ nie ustawienie spowodowałoby to python tylko do wyszukiwania atrybut dalej do MRO i oceń go na False
.
W związku z tym musimy sprawdzić w klasie wystąpienie, czy jedna z klas nadrzędnych ma __test__ = False
. Jeśli tak jest, a obecna definicja klasy nie ustawi sama w sobie , dodamy '__test__': True
do atrybutu dict.
Otrzymany kod wygląda następująco:
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)
Można by rozszerzyć to do jakiegoś bardziej niejawnego zachowania jak „jeśli nazwa zaczyna się Abstract
ustaw __test__ = False
automatycznie”, ale za to sam utrzymywać wyraźne przypisanie dla jasności.
Pozwól wklej proste unittests celu wykazania zachowanie - i jako przypomnienie, że każdy powinien podjąć dwie minuty, aby przetestować swój kod po wprowadzeniu funkcji.
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__')
Opcja '__test__ = FALSE Sztuką nie jest bardzo przydatna w przypadku klasy nadrzędnej, to ta siła dzieciom zajęcia jednoznacznego określenia' __test__ = TRUE lub byłoby ignorowane, że to niebezpieczne trik do użycia. – Guibod