首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何警告类(名称)被弃用

如何警告类(名称)被弃用
EN

Stack Overflow用户
提问于 2012-01-26 02:51:12
回答 6查看 29.4K关注 0票数 63

我已经重命名了一个python类,它是库的一部分。我愿意在一段时间内保留使用它以前的名字的可能性,但我想警告用户,它已经被弃用,将来将被删除。

我认为为了提供向后兼容性,使用这样的别名就足够了:

代码语言:javascript
复制
class NewClsName:
    pass

OldClsName = NewClsName

我不知道如何以一种优雅的方式将OldClsName标记为已弃用。也许我可以使OldClsName成为一个发出警告(到日志中)并根据其参数(使用*args**kvargs)构造NewClsName对象的函数,但是它看起来还不够优雅(或者它确实很优雅?)。

但是,我不知道Python标准库弃用警告是如何工作的。我想可能会有一些很好的魔法来处理弃用,例如,允许将其视为错误或静默,这取决于某个解释器的命令行选项。

问题是:如何警告用户使用过时的类别名(或者通常是过时的类)。

编辑:函数方法对我不起作用(我已经试过了),因为该类有一些类方法(工厂方法),当OldClsName被定义为函数时,这些方法不能被调用。以下代码将不起作用:

代码语言:javascript
复制
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( ... )

因为:

代码语言:javascript
复制
AttributeError: 'function' object has no attribute 'CreateVariant1'

继承是我唯一的选择吗?老实说,它在我看来并不是很干净-它通过引入不必要的派生来影响类的层次结构。此外,OldClsName is not NewClsName在大多数情况下不是问题,但在使用该库编写糟糕的代码的情况下可能会出现问题。

我还可以创建一个虚拟的、不相关的OldClsName类,并为其中的所有类方法实现一个构造函数和包装器,但在我看来,这是一个更糟糕的解决方案。

EN

回答 6

Stack Overflow用户

发布于 2012-01-26 02:54:21

请看一下warnings.warn

正如您将看到的,文档中的示例是一个弃用警告:

代码语言:javascript
复制
def deprecation(message):
    warnings.warn(message, DeprecationWarning, stacklevel=2)
票数 18
EN

Stack Overflow用户

发布于 2018-11-24 00:23:07

在python >= 3.6中,您可以轻松地处理子类化时的警告:

代码语言:javascript
复制
class OldClassName(NewClassName):
    def __init_subclass__(self):
        warn("Class has been renamed NewClassName", DeprecationWarning, 2)

重载__new__应该允许您在旧的类构造函数被直接调用时发出警告,但我没有测试过它,因为我现在不需要它。

票数 8
EN

Stack Overflow用户

发布于 2018-08-30 08:50:18

以下是解决方案应满足的要求列表:

不推荐使用的类的实例化应引发不推荐使用的类的warning

  • Subclassing应引发warning

  • Support isinstanceissubclass

解决方案

这可以通过自定义元类来实现:

代码语言:javascript
复制
class DeprecatedClassMeta(type):
    def __new__(cls, name, bases, classdict, *args, **kwargs):
        alias = classdict.get('_DeprecatedClassMeta__alias')

        if alias is not None:
            def new(cls, *args, **kwargs):
                alias = getattr(cls, '_DeprecatedClassMeta__alias')

                if alias is not None:
                    warn("{} has been renamed to {}, the alias will be "
                         "removed in the future".format(cls.__name__,
                             alias.__name__), DeprecationWarning, stacklevel=2)

                return alias(*args, **kwargs)

            classdict['__new__'] = new
            classdict['_DeprecatedClassMeta__alias'] = alias

        fixed_bases = []

        for b in bases:
            alias = getattr(b, '_DeprecatedClassMeta__alias', None)

            if alias is not None:
                warn("{} has been renamed to {}, the alias will be "
                     "removed in the future".format(b.__name__,
                         alias.__name__), DeprecationWarning, stacklevel=2)

            # Avoid duplicate base classes.
            b = alias or b
            if b not in fixed_bases:
                fixed_bases.append(b)

        fixed_bases = tuple(fixed_bases)

        return super().__new__(cls, name, fixed_bases, classdict,
                               *args, **kwargs)

    def __instancecheck__(cls, instance):
        return any(cls.__subclasscheck__(c)
            for c in {type(instance), instance.__class__})

    def __subclasscheck__(cls, subclass):
        if subclass is cls:
            return True
        else:
            return issubclass(subclass, getattr(cls,
                              '_DeprecatedClassMeta__alias'))

解释

DeprecatedClassMeta.__new__方法不仅为它所属的类调用,也为该类的每个子类调用。这样就有机会确保DeprecatedClass的任何实例都不会被实例化或子类化。

实例化很简单。元类重写DeprecatedClass__new__方法以始终返回NewClass的实例。

子类化并不是很难。DeprecatedClassMeta.__new__收到一个基类列表,需要用NewClass替换DeprecatedClass的实例。

最后,isinstanceissubclass检查是通过PEP 3119中定义的__instancecheck____subclasscheck__实现的。

测试

代码语言:javascript
复制
class NewClass:
    foo = 1


class NewClassSubclass(NewClass):
    pass


class DeprecatedClass(metaclass=DeprecatedClassMeta):
    _DeprecatedClassMeta__alias = NewClass


class DeprecatedClassSubclass(DeprecatedClass):
    foo = 2


class DeprecatedClassSubSubclass(DeprecatedClassSubclass):
    foo = 3


assert issubclass(DeprecatedClass, DeprecatedClass)
assert issubclass(DeprecatedClassSubclass, DeprecatedClass)
assert issubclass(DeprecatedClassSubSubclass, DeprecatedClass)
assert issubclass(NewClass, DeprecatedClass)
assert issubclass(NewClassSubclass, DeprecatedClass)

assert issubclass(DeprecatedClassSubclass, NewClass)
assert issubclass(DeprecatedClassSubSubclass, NewClass)

assert isinstance(DeprecatedClass(), DeprecatedClass)
assert isinstance(DeprecatedClassSubclass(), DeprecatedClass)
assert isinstance(DeprecatedClassSubSubclass(), DeprecatedClass)
assert isinstance(NewClass(), DeprecatedClass)
assert isinstance(NewClassSubclass(), DeprecatedClass)

assert isinstance(DeprecatedClassSubclass(), NewClass)
assert isinstance(DeprecatedClassSubSubclass(), NewClass)

assert NewClass().foo == 1
assert DeprecatedClass().foo == 1
assert DeprecatedClassSubclass().foo == 2
assert DeprecatedClassSubSubclass().foo == 3
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9008444

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档