ソリューションが満たす必要のある要件のリストは次のとおりです。
- 非推奨のクラスをインスタンス化すると、警告が表示されます
- 非推奨のクラスをサブクラス化すると、警告が表示されます
- サポート
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これにより、のインスタンスがインスタンス化またはサブクラス化されないようにする機会が与えられます。
インスタンス化は簡単です。メタクラスは、の__new__メソッドをオーバーライドして、DeprecatedClass常にのインスタンスを返しますNewClass。
サブクラス化はそれほど難しくありません。DeprecatedClassMeta.__new__基本クラスのリストを受け取り、のインスタンスをに置き換える必要がありDeprecatedClassますNewClass。
最後に、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