@nailxxの回答に対する1つの補遺:
__test__ = False
親クラスを設定してから、メタクラスを使用して(いくつかのすばらしい説明とともにこの質問を参照)、サブクラス化するときにTrueに戻すことができます。
(最後に、メタクラスを使用する言い訳を見つけました!)
は__test__
二重アンダースコア属性ですが、明示的に設定する必要True
があります。設定しないと、Pythonは属性をMROのさらに上で検索し、評価するだけになるためFalse
です。
したがって、クラスのインスタンス化時に、親クラスの1つにが含まれているかどうかを確認する必要があります__test__ = False
。これが当てはまり、現在のクラス定義がそれ自体を設定していない場合は、属性dictに__test__
追加します。'__test__': True
結果のコードは次のようになります。
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
ますが、わかりやすくするために、私自身は明示的な割り当てを維持します。
動作を示すために簡単な単体テストを貼り付けましょう。機能を導入した後、全員がコードをテストするのに2分かかることを思い出してください。
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__')