Ответ 1
Вероятно, вам нужен метакласс: метакласс просто определяет, как создается класс.
По умолчанию все классы создаются с использованием встроенного класса Python type
:
>>> class Foo:
... pass
...
>>> type(Foo)
<class 'type'>
>>> isinstance(Foo, type)
True
Таким образом, классы являются экземплярами type
.
Теперь мы можем подклассом type
создать пользовательский метакласс (класс, который создает классы):
class PatchMeta(type):
"""A metaclass to patch all inherited classes."""
Нам нужно контролировать создание наших классов, поэтому мы хотим переопределить type.__new__
здесь и использовать декоратор patch
для всех новых экземпляров:
class PatchMeta(type):
"""A metaclass to patch all inherited classes."""
def __new__(meta, name, bases, attrs):
cls = type.__new__(meta, name, bases, attrs)
cls = patch("some.core.function", mocked_method)(cls)
return cls
И теперь вы просто устанавливаете метакласс с помощью __metaclass__ = PatchMeta
:
class BaseTest(unittest.TestCase):
__metaclass__ = PatchMeta
# methods
Проблема заключается в этой строке:
cls = patch("some.core.function", mocked_method)(cls)
Поэтому в настоящее время мы всегда украшаем аргументы "some.core.function"
и mocked_method
.
Вместо этого вы можете сделать так, чтобы он использовал атрибуты класса, например:
cls = patch(*cls.patch_args)(cls)
И затем добавьте patch_args
в свои классы:
class BaseTest(unittest.TestCase):
__metaclass__ = PatchMeta
patch_args = ("some.core.function", mocked_method)
Изменить: Как отметил @mgilson в комментариях, patch()
изменяет методы класса вместо него, вместо того, чтобы возвращать новый класс. Из-за этого мы можем заменить __new__
на это __init__
:
class PatchMeta(type):
"""A metaclass to patch all inherited classes."""
def __init__(cls, *args, **kwargs):
super(PatchMeta, self).__init__(*args, **kwargs)
patch(*cls.patch_args)(cls)
Это совершенно беспроблемно.