dea*_*mon 58 python oop abstract-class scala
在Python中使用抽象属性实现以下Scala代码的最短/最优雅的方法是什么?
abstract class Controller {
val path: String
}
Run Code Online (Sandbox Code Playgroud)
Controller强制使用子类来定义Scala编译器的"路径".子类看起来像这样:
class MyController extends Controller {
override val path = "/home"
}
Run Code Online (Sandbox Code Playgroud)
Wto*_*wer 70
from abc import ABCMeta, abstractmethod
class A(metaclass=ABCMeta):
def __init__(self):
# ...
pass
@property
@abstractmethod
def a(self):
pass
@abstractmethod
def b(self):
pass
class B(A):
a = 1
def b(self):
pass
Run Code Online (Sandbox Code Playgroud)
未能声明a或b在派生类B中将引发TypeError如下:
TypeError:无法B使用抽象方法实例化抽象类a
有一个@abstractproperty装饰器:
from abc import ABCMeta, abstractmethod, abstractproperty
class A:
__metaclass__ = ABCMeta
def __init__(self):
# ...
pass
@abstractproperty
def a(self):
pass
@abstractmethod
def b(self):
pass
class B(A):
a = 1
def b(self):
pass
Run Code Online (Sandbox Code Playgroud)
小智 62
虽然在运行时之前不会遇到异常,但Python有一个内置的异常.
class Base(object):
@property
def path(self):
raise NotImplementedError
class SubClass(Base):
path = 'blah'
Run Code Online (Sandbox Code Playgroud)
Ser*_*ich 17
对于Python 3.3 + 有一个优雅的解决方案
from abc import ABC, abstractmethod
class BaseController(ABC):
@property
@abstractmethod
def path(self) -> str:
...
class Controller(BaseController):
path = "/home"
# Instead of an elipsis, you can add a docstring for clarity
class AnotherBaseController(ABC):
@property
@abstractmethod
def path(self) -> str:
"""
:return: the url path of this controller
"""
Run Code Online (Sandbox Code Playgroud)
尽管已经给出了一些很好的答案,但我认为这个答案仍然会增加一些价值。这种方法有两个优点:
...在抽象方法的主体中比pass. 与pass,...意味着没有操作,其中pass仅意味着没有实际实现
...比扔更推荐NotImplementedError(...)。如果子类中缺少抽象字段的实现,这会自动提示极其详细的错误。相比之下,NotImplementedError它本身并没有说明为什么缺少实现。此外,它需要手工劳动才能真正抬起它。
Jam*_*mes 13
自从最初提出这个问题以来,python已经改变了抽象类的实现方式。我在python 3.6中使用abc.ABC形式主义使用了稍微不同的方法。在这里,我将常量定义为必须在每个子类中定义的属性。
from abc import ABC, abstractmethod
class Base(ABC):
@property
@classmethod
@abstractmethod
def CONSTANT(cls):
return NotImplementedError
def print_constant(self):
print(type(self).CONSTANT)
class Derived(Base):
CONSTANT = 42
Run Code Online (Sandbox Code Playgroud)
这将强制派生类定义常量,否则TypeError当您尝试实例化子类时将引发异常。如果要将常量用于抽象类中实现的任何功能,则必须使用type(self).CONSTANT而不是just 访问子类常量CONSTANT,因为该值在基类中未定义。
还有其他方法可以实现此功能,但是我喜欢这种语法,因为对我来说,这似乎对读者来说是最简单和显而易见的。
先前的答案都触及了有用的观点,但是我认为被接受的答案并不能直接回答问题,因为
CONSTANT未定义子类,则在实例化子类时会导致运行时错误。接受的答案允许实例化该对象,并且仅在CONSTANT访问该对象时抛出错误,从而使执行不太严格。 这不是对原始答案的错。自发布以来,对抽象类语法进行了重大更改,在这种情况下,它可以实现更整洁,更实用的实现。
jon*_*ach 11
从 Python 3.6 开始,您可以使用__init_subclass__在初始化时检查子类的类变量:
from abc import ABC
class A(ABC):
@classmethod
def __init_subclass__(cls):
required_class_variables = [
"foo",
"bar"
]
for var in required_class_variables:
if not hasattr(cls, var):
raise NotImplementedError(
f'Class {cls} lacks required `{var}` class attribute'
)
Run Code Online (Sandbox Code Playgroud)
如果未定义缺少的类变量,则在初始化子类时会引发错误,因此您不必等到访问缺少的类变量。
In Python 3.6+, you can annotate an attribute of an abstract class (or any variable) without providing a value for that attribute.
class Controller:
path: str
class MyController(Controller):
path = "/home"
Run Code Online (Sandbox Code Playgroud)
This makes for very clean code where it is obvious that the attribute is abstract. Code that tries to access the attribute when if has not been overwritten will raise an AttributeError.
您可以在abc.ABC抽象基类中创建一个属性,其值NotImplemented如此,如果该属性未被覆盖然后使用,则会在运行时显示错误.
以下代码使用PEP 484类型提示来帮助PyCharm正确地静态分析path属性的类型.
import abc
class Controller(abc.ABC):
path = NotImplemented # type: str
class MyController(Controller):
path = '/home'
Run Code Online (Sandbox Code Playgroud)
您的基类可以实现一个__new__检查类属性的方法:
class Controller(object):
def __new__(cls, *args, **kargs):
if not hasattr(cls,'path'):
raise NotImplementedError("'Controller' subclasses should have a 'path' attribute")
return object.__new__(cls)
class C1(Controller):
path = 42
class C2(Controller):
pass
c1 = C1()
# ok
c2 = C2()
# NotImplementedError: 'Controller' subclasses should have a 'path' attribute
Run Code Online (Sandbox Code Playgroud)
这样错误会在实例化时引发
Python3.6 实现可能如下所示:
In [20]: class X:
...: def __init_subclass__(cls):
...: if not hasattr(cls, 'required'):
...: raise NotImplementedError
In [21]: class Y(X):
...: required = 5
...:
In [22]: Y()
Out[22]: <__main__.Y at 0x7f08408c9a20>
Run Code Online (Sandbox Code Playgroud)
我对@James 的回答做了一点修改,这样所有这些装饰器就不会占据太多位置。如果你有多个这样的抽象属性要定义,这很方便:
from abc import ABC, abstractmethod
def abstractproperty(func):
return property(classmethod(abstractmethod(func)))
class Base(ABC):
@abstractproperty
def CONSTANT(cls): ...
def print_constant(self):
print(type(self).CONSTANT)
class Derived(Base):
CONSTANT = 42
class BadDerived(Base):
BAD_CONSTANT = 42
Derived() # -> Fine
BadDerived() # -> Error
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
37610 次 |
| 最近记录: |