相当于Python中字段的NotImplementedError
在Python2.x中,当您想将一个方法标记为抽象时,可以这样定义它:相当于Python中字段的NotImplementedError,python,abstract-class,Python,Abstract Class,在Python2.x中,当您想将一个方法标记为抽象时,可以这样定义它: class Base: def foo(self): raise NotImplementedError("Subclasses should implement this!") 然后,如果你忘记覆盖它,你会得到一个很好的提醒异常。是否有一种等效的方法将字段标记为抽象字段?或者在类docstring中声明它是您唯一能做的吗 起初我以为可以将字段设置为NotImplemented,但当我查找它的实际用
class Base:
def foo(self):
raise NotImplementedError("Subclasses should implement this!")
然后,如果你忘记覆盖它,你会得到一个很好的提醒异常。是否有一种等效的方法将字段标记为抽象字段?或者在类docstring中声明它是您唯一能做的吗
起初我以为可以将字段设置为NotImplemented,但当我查找它的实际用途(丰富的比较)时,它似乎是滥用的。是的,可以。使用
@属性
装饰器。例如,如果您有一个名为“示例”的字段,那么您不能执行以下操作:
class Base(object):
@property
def example(self):
raise NotImplementedError("Subclasses should implement this!")
运行以下命令会产生一个NotImplementedError
,就像您想要的那样
b = Base()
print b.example
请注意,类类型传递到require\u abstract\u字段
,因此,如果多个继承类使用此字段,它们不会全部验证派生最多的类的字段。你也许可以用元类自动完成,但我没有深入研究。将字段定义为“无”是可以接受的。备选答案:
@property
def NotImplementedField(self):
raise NotImplementedError
class a(object):
x = NotImplementedField
class b(a):
# x = 5
pass
b().x
a().x
这与Evan的类似,但简洁且便宜——您只能得到一个NotImplementedField实例。下面是我的解决方案:
def未实现方法(func):
从functools导入包装
从检查导入getargspec,formatargspec
@包装(func)
def包装器(自身、*args、**kwargs):
c=自我类名称__
m=函数名__
a=格式argspec(*getargspec(func))
raise NOTEImplementedError(“\'%s\”对象未实现方法\'%s%s\'”(c、m、a))
返回包装器
def未实现属性(func):
从functools导入包装
从检查导入getargspec,formatargspec
@包装(func)
def包装器(自身、*args、**kwargs):
c=自我类名称__
m=函数名__
raise NOTEImplementedError(“\'%s\”对象未实现属性\'%s\'”%(c,m))
返回属性(包装器、包装器、包装器)
它可以用作
类抽象库(对象):
@未实现的方法
def测试(自我):
通过
@未实现的属性
def值(自身):
通过
类实现(AbstractBase):
值=无
定义初始化(自):
self.value=42
def测试(自我):
返回真值
更好的方法是使用:
请注意,您仍然应该使用
raisenotimplementederror
而不是类似于pass
的东西,因为没有任何东西阻止继承类调用super().demo\u method()
,如果抽象demo\u method
只是pass
,则,这将以静默方式失败。处理此问题的一个有趣模式是在父类中将属性设置为None
,并使用确保已在子类中设置属性的函数访问该属性
以下是一个例子:
这个问题似乎对实例属性和类属性都是开放的,我将只关注第一个主题 因此,例如属性,另一种解决方法是使用以下方法定义必填字段: 屈服
pyfields.core.MandatoryFieldInitError:
Mandatory field 'example' has not been initialized yet
on instance <__main__.Base object at 0x000002C1000C0C18>.
pyfields.core.MandatoryFieldInitError:
必填字段“示例”尚未初始化
例如。
当然,它不能通过谈论子类来编辑错误消息。但在某种程度上,不谈论子类更为现实——事实上,在python中,可以在基类的实例上覆盖属性,而不仅仅是在子类中
注意:我是
pyfields
的作者。有关详细信息,请参阅。下面是一个简单的示例,说明如何在Python 3中为子类设置所需的属性/方法
class Base:
requires = ('foo', 'bar')
def __init_subclass__(cls, **kwargs):
for requirement in cls.requires:
if not hasattr(cls, requirement):
raise NotImplementedError(
f'"{cls.__name__}" must have "{requirement}".')
super().__init_subclass__(**kwargs)
它仍然有效,即使它的初衷是为了丰富的比较。有什么问题吗?第一个问题是您可以从对象(myvar=Base.field)读取字段,接下来您知道的是,在其他部分尝试使用它并获得一个神秘的AttributeError之前,到处都没有实现。第二个问题是,在我看来,它妨碍了可读性(“那个丰富的比较在做什么?我遗漏了什么吗?”埃文的解决方案以一种熟悉的方式准确地表达了正在发生的事情。@Kiv:请不要对你的问题发表评论。请用您提出的具体观点更新您的问题。相关:这更简单,但我喜欢我的版本立即抛出的方式,不仅仅是在使用属性的情况下。但是Glenn,如果属性
示例
在其他点设置了怎么办?如果它立即抛出,那么它可能永远不会有机会通过其他方式设置。请记住,字段和方法可以在任何时候设置为类,而不仅仅是在定义类的时候。啊,这就是我所追求的。如果我定义的基类希望用户定义方法(或字段),我希望它在基类处于活动状态的任何时候都被定义,从init开始。我会考虑以后将其定义为一个错误,因为我可能想从init访问它们。当然,您可以用不同的规则定义类,但这似乎是最清晰的。(当然,我最初是一个C++程序员,我喜欢好的、严格的、定义良好的接口规则。)格伦,两个评论:听起来像C++哲学,而Python哲学则更宽松——你只关心当你尝试访问一个属性时你得到了什么。当您不尝试访问它时,它可以是任何内容,包括未定义的内容。(当然,这不是必需的,这只是许多Python代码的编写方式,我认为语言创建者/维护者的意图就是这样。)聪明,Glenn.:)我能看到的唯一缺点是,当抛出NotImplementedError时,您不能指定要显示的不同消息。您可以将NotImplementedField定义为一个函数,用于显示消息。你得聪明一点,用单曲来保持它
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
from pyfields import field
class Base(object):
example = field(doc="This should contain an example.")
b = Base()
b.example
pyfields.core.MandatoryFieldInitError:
Mandatory field 'example' has not been initialized yet
on instance <__main__.Base object at 0x000002C1000C0C18>.
class Base:
requires = ('foo', 'bar')
def __init_subclass__(cls, **kwargs):
for requirement in cls.requires:
if not hasattr(cls, requirement):
raise NotImplementedError(
f'"{cls.__name__}" must have "{requirement}".')
super().__init_subclass__(**kwargs)