@nailxx 答案的一个附录:
您可以__test__ = False
在父类中设置,然后在子类化时使用元类(请参阅此问题以及一些精彩的解释)将其设置回 True 。
(最后,我找到了使用元类的借口!)
虽然__test__
是双下划线属性,但我们必须明确将其设置为,因为不设置它会导致 python 只是在MROTrue
中进一步查找该属性并将其评估为。False
因此,我们需要在类实例化时检查父类之一是否具有__test__ = False
. 如果是这种情况并且当前类定义尚未设置__test__
,我们将添加'__test__': True
到属性 dict。
生成的代码如下所示:
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)
可以将其扩展到一些更隐含的行为,例如“如果名称以 . 开头Abstract
,则__test__ = False
自动设置”,但为了清楚起见,我自己会保留显式分配。
让我粘贴简单的单元测试来演示行为 - 并提醒每个人在引入功能后应该花两分钟测试他们的代码。
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__')