代码之家  ›  专栏  ›  技术社区  ›  Kiv

python中字段的notimplementederror等效值

  •  43
  • Kiv  · 技术社区  · 15 年前

    在python 2.x中,当您想要将一个方法标记为抽象的时候,您可以这样定义它:

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

    如果你忘记覆盖它,你会得到一个很好的提醒例外。是否存在将字段标记为抽象字段的等效方法?或者是在类docstring中声明它,这是您所能做的吗?

    起初我以为我可以将字段设置为NotImplemented,但当我查找它的实际用途(丰富的比较)时,它似乎是一种滥用。

    6 回复  |  直到 6 年前
        1
  •  45
  •   Evan Fosmark    15 年前

    是的,你可以。使用 @property 装饰者。例如,如果您有一个名为“example”的字段,那么您不能这样做:

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

    运行以下命令将生成 NotImplementedError 就像你想要的。

    b = Base()
    print b.example
    
        2
  •  30
  •   Glenn Maynard    15 年前

    备选答案:

    @property
    def NotImplementedField(self):
        raise NotImplementedError
    
    class a(object):
        x = NotImplementedField
    
    class b(a):
        # x = 5
        pass
    
    b().x
    a().x
    

    这类似于Evan的,但是简洁而廉价——您只会得到一个NotImplementedField的实例。

        3
  •  2
  •   Glenn Maynard    15 年前
    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 ,因此,如果多个继承类使用此方法,它们不会全部验证最派生类的字段。您也许可以用一个元类来实现这一点的自动化,但我没有深入研究。接受将字段定义为“无”。

        4
  •  2
  •   ostrokach    8 年前

    更好的方法是使用 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
    

    注意,你应该 raise NotImplementedError 而不是像 pass ,因为没有什么阻止继承类调用 super().demo_method() 如果是摘要 demo_method 只是 通过 ,这将无声地失败。

        5
  •  0
  •   fwyzard    9 年前

    下面是我的解决方案:

    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
    
        6
  •  0
  •   moppag    6 年前

    要处理此问题,一个有趣的模式是将属性设置为 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