Python 2.xでは、メソッドを抽象としてマークする場合、次のように定義できます。
class Base:
def foo(self):
raise NotImplementedError("Subclasses should implement this!")
次に、それをオーバーライドするのを忘れた場合は、Niceリマインダー例外が発生します。フィールドを抽象としてマークする同等の方法はありますか?それとも、クラスdocstringでそれを述べて、あなたができることはすべてですか?
最初はこのフィールドをNotImplementedに設定できると思っていましたが、実際に何を使用するかを調べたところ(豊富な比較)、乱用のように見えました。
はい、できます。使用 @property
デコレータ。たとえば、「example」というフィールドがある場合、次のようなことはできません。
class Base(object):
@property
def example(self):
raise NotImplementedError("Subclasses should implement this!")
以下を実行すると、希望どおりにNotImplementedError
が生成されます。
b = Base()
print b.example
別の答え:
@property
def NotImplementedField(self):
raise NotImplementedError
class a(object):
x = NotImplementedField
class b(a):
# x = 5
pass
b().x
a().x
これはEvanと似ていますが、簡潔で安価です。NotImplementedFieldの単一のインスタンスしか取得できません。
これを行うためのより良い方法は Abstract Base Classes を使用することです:
_import abc
class Foo(abc.ABC):
@property
@abc.abstractmethod
def demo_attribute(self):
raise NotImplementedError
@abc.abstractmethod
def demo_method(self):
raise NotImplementedError
class BadBar(Foo):
pass
class GoodBar(Foo):
demo_attribute = 'yes'
def demo_method(self):
return self.demo_attribute
bad_bar = BadBar()
# TypeError: Can't instantiate abstract class BadBar \
# with abstract methods demo_attribute, demo_method
good_bar = GoodBar()
# OK
_
継承するクラスがsuper().demo_method()
を呼び出すのを妨げるものは何もないので、pass
などの代わりに_raise NotImplementedError
_がまだあるはずです。抽象_demo_method
_がpass
の場合、これは黙って失敗する。
def require_abstract_fields(obj, cls):
abstract_fields = getattr(cls, "abstract_fields", None)
if abstract_fields is None:
return
for field in abstract_fields:
if not hasattr(obj, field):
raise RuntimeError, "object %s failed to define %s" % (obj, field)
class a(object):
abstract_fields = ("x", )
def __init__(self):
require_abstract_fields(self, a)
class b(a):
abstract_fields = ("y", )
x = 5
def __init__(self):
require_abstract_fields(self, b)
super(b, self).__init__()
b()
a()
クラス型をrequire_abstract_fields
に渡すことに注意してください。継承された複数のクラスがこれを使用する場合、すべてのクラスが最も派生したクラスのフィールドを検証するわけではありません。あなたはメタクラスでこれを自動化できるかもしれませんが、私はそれを掘り下げませんでした。フィールドをNoneに定義することは受け入れられます。
そしてこれが私の解決策です:
def not_implemented_method(func):
from functools import wraps
from inspect import getargspec, formatargspec
@wraps(func)
def wrapper(self, *args, **kwargs):
c = self.__class__.__name__
m = func.__name__
a = formatargspec(*getargspec(func))
raise NotImplementedError('\'%s\' object does not implement the method \'%s%s\'' % (c, m, a))
return wrapper
def not_implemented_property(func):
from functools import wraps
from inspect import getargspec, formatargspec
@wraps(func)
def wrapper(self, *args, **kwargs):
c = self.__class__.__name__
m = func.__name__
raise NotImplementedError('\'%s\' object does not implement the property \'%s\'' % (c, m))
return property(wrapper, wrapper, wrapper)
それはとして使用することができます
class AbstractBase(object):
@not_implemented_method
def test(self):
pass
@not_implemented_property
def value(self):
pass
class Implementation(AbstractBase):
value = None
def __init__(self):
self.value = 42
def test(self):
return True
これを処理する興味深いパターンは、親クラスで属性をNone
に設定し、属性が子クラスで設定されていることを確認する関数で属性にアクセスすることです。
Django-rest-framework の例を次に示します。
class GenericAPIView(views.APIView):
[...]
serializer_class = None
[...]
def get_serializer_class(self):
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)
return self.serializer_class