我已经重命名了一个python类,它是库的一部分。我愿意在一段时间内保留使用它以前的名字的可能性,但我想警告用户,它已经被弃用,将来将被删除。
我认为为了提供向后兼容性,使用这样的别名就足够了:
class NewClsName:
pass
OldClsName = NewClsName
我不知道如何以一种优雅的方式将OldClsName
标记为已弃用。也许我可以使OldClsName
成为一个发出警告(到日志中)并根据其参数(使用*args
和**kvargs
)构造NewClsName
对象的函数,但是它看起来还不够优雅(或者它确实很优雅?)。
但是,我不知道Python标准库弃用警告是如何工作的。我想可能会有一些很好的魔法来处理弃用,例如,允许将其视为错误或静默,这取决于某个解释器的命令行选项。
问题是:如何警告用户使用过时的类别名(或者通常是过时的类)。
编辑:函数方法对我不起作用(我已经试过了),因为该类有一些类方法(工厂方法),当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
类,并为其中的所有类方法实现一个构造函数和包装器,但在我看来,这是一个更糟糕的解决方案。
发布于 2012-01-26 02:54:21
请看一下warnings.warn
。
正如您将看到的,文档中的示例是一个弃用警告:
def deprecation(message):
warnings.warn(message, DeprecationWarning, stacklevel=2)
发布于 2018-11-24 00:23:07
在python >= 3.6中,您可以轻松地处理子类化时的警告:
class OldClassName(NewClassName):
def __init_subclass__(self):
warn("Class has been renamed NewClassName", DeprecationWarning, 2)
重载__new__
应该允许您在旧的类构造函数被直接调用时发出警告,但我没有测试过它,因为我现在不需要它。
发布于 2018-08-30 08:50:18
以下是解决方案应满足的要求列表:
不推荐使用的类的实例化应引发不推荐使用的类的warning
isinstance
和issubclass
解决方案
这可以通过自定义元类来实现:
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
的实例。
最后,isinstance
和issubclass
检查是通过PEP 3119中定义的__instancecheck__
和__subclasscheck__
实现的。
测试
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
https://stackoverflow.com/questions/9008444
复制相似问题