Подтвердить что ты не робот

Как предупредить о стирании класса (имени)

Я переименовал класс 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 и реализовать конструктор, а также обертки для всех методов класса в нем, но, на мой взгляд, это еще хуже.

4b9b3361

Ответ 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)