un addendum alla risposta 's @nailxx:
È possibile impostare __test__ = False
nella classe padre e poi utilizzare un metaclasse (vedi This question con alcune spiegazioni brillanti) per impostare di nuovo a True quando sottoclasse.
(Infine, ho trovato una scusa per usare un metaclasse!)
Anche se __test__
è un attributo doppia sottolineatura, dobbiamo impostare in modo esplicito a True
, dal momento che non l'impostazione causerebbe python solo per ricerca l'attributo continua su MRO e lo valuta su False
.
Pertanto, è necessario verificare all'istanza della classe se una delle classi padre ha __test__ = False
. Se questo è il caso e la definizione della classe corrente non ha impostato lo stesso __test__
, aggiungeremo '__test__': True
agli attributi dict.
Il codice risultante è simile al seguente:
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)
Si potrebbe estendere questo per alcuni comportamenti più implicito del tipo “se il nome inizia con Abstract
, impostare __test__ = False
automaticamente”, ma per me sarebbe mantenere l'assegnazione esplicita per chiarezza.
Permettetemi di incollare Unittests semplici per dimostrare il comportamento - e per ricordare che tutti dovrebbero prendere i due minuti per testare la loro codice dopo l'introduzione di una funzione.
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__')
Il trucco '__test__ = FALSE non è molto utile nel caso di una classe genitore, questo costringerebbe i bambini classi specificare esplicitamente' __test__ = true' o sarebbe essere ignorato, che è un pericoloso trucco da usare. – Guibod