Как предупредить о стирании класса (имени)
Я переименовал класс python, который является частью библиотеки. Я готов оставить возможность использовать свое предыдущее имя некоторое время, но хотел бы предупредить пользователя о том, что он устарел и будет удален в будущем.
Я думаю, что для обеспечения обратной совместимости будет достаточно использовать такой псевдоним:
class NewClsName:
pass
OldClsName = NewClsName
Я понятия не имею, как пометить OldClsName
как устаревшее элегантным способом. Возможно, я мог бы сделать OldClsName
функцию, которая выдает предупреждение (для журналов) и строит объект NewClsName
из своих параметров (используя *args
и **kvargs
), но он не выглядит достаточно элегантным (или, может быть,?).
Однако я не знаю, как работают предупреждения об устаревании стандартной библиотеки Python. Я предполагаю, что может быть какая-то приятная магия, чтобы справиться с устареванием, например. позволяя рассматривать его как ошибки или молчание в зависимости от опции командной строки интерпретатора.
Возникает вопрос: как предупредить пользователей об использовании устаревшего псевдонима класса (или устаревшего класса в целом).
EDIT: функциональный подход не работает для меня (я уже попробовал), потому что класс имеет некоторые методы класса (методы factory), которые нельзя вызвать, когда OldClsName
определяется как функция. Следующий код не будет работать:
class NewClsName(object):
@classmethod
def CreateVariant1( cls, ... ):
pass
@classmethod
def CreateVariant2( cls, ... ):
pass
def OldClsName(*args, **kwargs):
warnings.warn("The 'OldClsName' class was renamed [...]",
DeprecationWarning )
return NewClsName(*args, **kwargs)
OldClsName.CreateVariant1( ... )
Из-за:
AttributeError: 'function' object has no attribute 'CreateVariant1'
Наследование - единственный вариант? Честно говоря, это не выглядит очень чистым для меня - это влияет на иерархию классов посредством введения ненужного вывода. Кроме того, OldClsName is not NewClsName
то, что не является проблемой в большинстве случаев, но может быть проблемой в случае плохо написанного кода с использованием библиотеки.
Я мог бы также создать фиктивный, несвязанный класс OldClsName
и реализовать конструктор, а также обертки для всех методов класса в нем, но, на мой взгляд, это еще хуже.
Ответы
Ответ 1
Возможно, я мог бы сделать OldClsName функцией, которая выдает предупреждение (to log) и создает объект NewClsName из его параметров (используя * args и ** kvargs), но он не кажется достаточно элегантным (или, может быть, это?).
Да, я думаю, что довольно стандартная практика:
def OldClsName(*args, **kwargs):
from warnings import warn
warn("get with the program!")
return NewClsName(*args, **kwargs)
Единственное сложное дело в том, что если у вас есть вещи, относящиеся к подклассу от OldClsName
, тогда нам нужно умнее. Если вам просто нужно сохранить доступ к методам класса, это должно сделать это:
class DeprecationHelper(object):
def __init__(self, new_target):
self.new_target = new_target
def _warn(self):
from warnings import warn
warn("Get with the program!")
def __call__(self, *args, **kwargs):
self._warn()
return self.new_target(*args, **kwargs)
def __getattr__(self, attr):
self._warn()
return getattr(self.new_target, attr)
OldClsName = DeprecationHelper(NewClsName)
Я не тестировал его, но это должно дать вам идею - __call__
будет обрабатывать маршрут обычной маршрутизации, __getattr__
будет захватывать обращения к методам класса и все равно генерировать предупреждение, не вступая в ваш класс иерархия.
Ответ 2
Пожалуйста, посмотрите warnings.warn
.
Как вы увидите, пример в документации представляет собой предупреждение об отказе:
def deprecation(message):
warnings.warn(message, DeprecationWarning, stacklevel=2)
Ответ 3
Почему вы не просто подкласс? Таким образом, не следует нарушать код пользователя.
class OldClsName(NewClsName):
def __init__(self, *args, **kwargs):
warnings.warn("The 'OldClsName' class was renamed [...]",
DeprecationWarning)
NewClsName.__init__(*args, **kwargs)