web-dev-qa-db-ja.com

PythonのフィールドのNotImplementedErrorと同等

Python 2.xでは、メソッドを抽象としてマークする場合、次のように定義できます。

class Base:
    def foo(self):
        raise NotImplementedError("Subclasses should implement this!")

次に、それをオーバーライドするのを忘れた場合は、Niceリマインダー例外が発生します。フィールドを抽象としてマークする同等の方法はありますか?それとも、クラスdocstringでそれを述べて、あなたができることはすべてですか?

最初はこのフィールドをNotImplementedに設定できると思っていましたが、実際に何を使用するかを調べたところ(豊富な比較)、乱用のように見えました。

45
Kiv

はい、できます。使用 @propertyデコレータ。たとえば、「example」というフィールドがある場合、次のようなことはできません。

class Base(object):

    @property
    def example(self):
        raise NotImplementedError("Subclasses should implement this!")

以下を実行すると、希望どおりにNotImplementedErrorが生成されます。

b = Base()
print b.example
46
Evan Fosmark

別の答え:

@property
def NotImplementedField(self):
    raise NotImplementedError

class a(object):
    x = NotImplementedField

class b(a):
    # x = 5
    pass

b().x
a().x

これはEvanと似ていますが、簡潔で安価です。NotImplementedFieldの単一のインスタンスしか取得できません。

31
Glenn Maynard

これを行うためのより良い方法は 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の場合、これは黙って失敗する。

3
ostrokach
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に定義することは受け入れられます。

2
Glenn Maynard

そしてこれが私の解決策です:

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
0
fwyzard

これを処理する興味深いパターンは、親クラスで属性を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
0
moppag