Python 中的抽象属性
在 Python 中使用抽象属性实现以下 Scala 代码的最短/最优雅的方法是什么?
abstract class Controller {
val path: String
}
Scala 编译器强制 Controller
的子类定义“路径”。子类看起来像这样:
class MyController extends Controller {
override val path = "/home"
}
What is the shortest / most elegant way to implement the following Scala code with an abstract attribute in Python?
abstract class Controller {
val path: String
}
A subclass of Controller
is enforced to define "path" by the Scala compiler. A subclass would look like this:
class MyController extends Controller {
override val path = "/home"
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
Python 3.3+
未能在派生类
B
中声明a
或b
将引发TypeError
,例如:Python 2.7
有一个 @abstractproperty 装饰器:
Python 3.3+
Failure to declare
a
orb
in the derived classB
will raise aTypeError
such as:Python 2.7
There is an @abstractproperty decorator for this:
自从这个问题最初被提出以来,python 改变了抽象类的实现方式。我在 python 3.6 中使用了 abc.ABC 形式主义,使用了稍微不同的方法。这里我将常量定义为必须在每个子类中定义的属性。
这会强制派生类定义常量,否则当您尝试实例化子类时将引发
TypeError
异常。当您想要将常量用于抽象类中实现的任何功能时,必须通过type(self).CONSTANT
访问子类常量,而不仅仅是CONSTANT
,因为值在基类中未定义。还有其他方法可以实现此目的,但我喜欢这种语法,因为在我看来,它对读者来说是最简单明了的。
前面的答案都触及了有用的要点,但我觉得接受的答案并没有直接回答问题,因为
CONSTANT
时抛出错误,从而使执行不那么严格。这并不是要指责原来的答案。自发布以来,抽象类语法发生了重大变化,在本例中允许更简洁、更实用的实现。
Since this question was originally asked, python has changed how abstract classes are implemented. I have used a slightly different approach using the abc.ABC formalism in python 3.6. Here I define the constant as a property which must be defined in each subclass.
This forces the derived class to define the constant, or else a
TypeError
exception will be raised when you try to instantiate the subclass. When you want to use the constant for any functionality implemented in the abstract class, you must access the subclass constant bytype(self).CONSTANT
instead of justCONSTANT
, since the value is undefined in the base class.There are other ways to implement this, but I like this syntax as it seems to me the most plain and obvious for the reader.
The previous answers all touched useful points, but I feel the accepted answer does not directly answer the question because
CONSTANT
is not defined. The accepted answer allows the object to be instantiated and only throws an error whenCONSTANT
is accessed, making the enforcement less strict.This is not to fault the original answers. Major changes to the abstract class syntax have occurred since they were posted, which in this case allow a neater and more functional implementation.
Python 为此有一个内置异常,尽管直到运行时您才会遇到该异常。
Python has a built-in exception for this, though you won't encounter the exception until runtime.
在 Python 3.6+ 中,您可以注释抽象类的属性(或任何变量)而不提供该属性的值。
这使得代码非常干净,很明显该属性是抽象的。
应该注意的是,如果子类不提供实现,则不会在定义时引发异常。但是,如果任何东西尝试访问未定义的属性,则会引发 AttributeError 异常。
In Python 3.6+, you can annotate an attribute of an abstract class (or any variable) without providing a value for that attribute.
This makes for very clean code where it is obvious that the attribute is abstract.
It should be noted that this will not raise an exception at definition time if a subclass does not provide an implementation. However, an
AttributeError
exception will be raised if anythhing tries to access the undefined attribute.您可以在 abc.ABC 抽象基类中创建一个属性其值例如
NotImplemented
这样,如果该属性未被覆盖然后使用,则会在运行时显示一个明确的错误,表明其意图。以下代码使用 PEP 484 类型提示来帮助 PyCharm 正确地静态分析
path
属性的类型。You could create an attribute in the abc.ABC abstract base class with a value such as
NotImplemented
so that if the attribute is not overriden and then used, a clear error that expresses intent is shown at run time.The following code uses a PEP 484 type hint to help PyCharm correctly statically analyze the type of the
path
attribute as well.从 Python 3.6 开始,您可以使用
__init_subclass__
在初始化时检查子类的类变量:如果未定义缺少的类变量,这会在子类初始化时引发错误,因此您不必等到访问缺失的类变量。
As of Python 3.6 you can use
__init_subclass__
to check for the class variables of the child class upon initialisation:This raises an Error on initialisation of the child class, if the missing class variable is not defined, so you don't have to wait until the missing class variable would be accessed.
对于 Python 3.3+ 有一个优雅的解决方案
尽管已经给出了一些很好的答案,但我认为这个答案仍然会增加一些价值。这种方法有两个优点:
...
比pass
更可取。与pass
不同,...
意味着无操作,其中pass
仅表示缺少实际实现比抛出
NotImplementedError(...)
更推荐使用...
。如果子类中缺少抽象字段的实现,这会自动提示极其详细的错误。相反,NotImplementedError
本身并没有说明为什么缺少实现。而且,实际饲养它还需要体力劳动。For Python 3.3+ there's an elegant solution
Despite some great answers have already been given, I thought this answer would nevertheless add some value. This approach has two advantages:
...
in an abstract method's body is more preferable thanpass
. Unlikepass
,...
implies no operations, wherepass
only means the absence of an actual implementation...
is more recommended than throwingNotImplementedError(...)
. This automatically prompts an extremely verbose error if the implementation of an abstract field is missing in a subclass. In contrast,NotImplementedError
itself doesn't tell why the implementation is missing. Moreover, it requires manual labor to actually raise it.我修改了一点 @James 答案,这样所有这些装饰器就不会占据太多位置。如果您有多个这样的抽象属性要定义,这很方便:
I've modified just a bit @James answer, so that all those decorators do not take so much place. If you had multiple such abstract properties to define, this is handy:
Python3.6 的实现可能如下所示:
Python3.6 implementation might looks like this:
您的基类可以实现一个 __new__ 方法来检查类属性:
这样在实例化时会引发错误
Your base class could implement a
__new__
method that check for class attribute:This way the error raise at instantiation
我认为从 3.3 开始,abc.abstractproperty 已被弃用。
As of 3.3
abc.abstractproperty
is deprecated, I think.Bastien Léonard 的答案提到了抽象基类模块,而 Brendan Abel 的答案则涉及引发错误的未实现属性。为了确保该类不会在模块外部实现,您可以在基本名称前加上下划线,表示它是模块私有的(即不导入)。
IE
Bastien Léonard's answer mentions the abstract base class module and Brendan Abel's answer deals with non-implemented attributes raising errors. To ensure that the class is not implemented outside of the module, you could prefix the base name with an underscore which denotes it as private to the module (i.e. it is not imported).
i.e.
看一下 abc(抽象基类)模块: http://docs.python.org /library/abc.html
但是,在我看来,最简单和最常见的解决方案是在创建基类的实例或访问其属性时引发异常。
Have a look at the abc (Abtract Base Class) module: http://docs.python.org/library/abc.html
However, in my opinion the simplest and most common solution is to raise an exception when an instance of the base class is created, or when its property is accessed.