Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python中的抽象属性_Python_Oop_Scala_Abstract Class - Fatal编程技术网

Python中的抽象属性

Python中的抽象属性,python,oop,scala,abstract-class,Python,Oop,Scala,Abstract Class,在Python中用抽象属性实现以下Scala代码的最短/最优雅的方法是什么 abstract class Controller { val path: String } Scala编译器强制执行控制器的子类来定义“路径”。子类如下所示: class MyController extends Controller { override val path = "/home" } In [20]: class X: ...: def __init_subcl

在Python中用抽象属性实现以下Scala代码的最短/最优雅的方法是什么

abstract class Controller {

    val path: String

}
Scala编译器强制执行
控制器的子类来定义“路径”。子类如下所示:

class MyController extends Controller {

    override val path = "/home"

}
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>

Bastien Léonard的回答提到了抽象基类模块,Brendan Abel的回答涉及引发错误的未实现属性。为了确保该类不是在模块外部实现的,您可以在基名称前面加下划线,表示它是模块的私有名称(即不导入)

i、 e

请看abc(Abtract基类)模块:


然而,在我看来,最简单也是最常见的解决方案是在创建基类的实例或访问其属性时引发异常。

Python对此有一个内置的异常,尽管在运行时之前不会遇到异常

class Base(object):
    @property
    def path(self):
        raise NotImplementedError


class SubClass(Base):
    path = 'blah'

您的基类可以实现一个检查类属性的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuu
方法:

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

这样,在实例化时会出现错误

您可以在抽象基类中创建一个属性,该属性的值如下:如果该属性未被重写然后使用,则在运行时会显示错误

下面的代码使用类型提示帮助PyCharm正确地静态分析
path
属性的类型

from abc import ABC


class Controller(ABC):
    path: str = NotImplemented

class MyController(Controller):
    path = "/home"
Python 3.3+ 未能在派生类
b
中声明
a
b
将引发
TypeError
,例如:

TypeError
:无法使用抽象方法
a
实例化抽象类
B

Python 2.7 这里有一个装饰师:

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

Python3.6的实现可能如下所示:

class MyController extends Controller {

    override val path = "/home"

}
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>
[20]中的
:X类:
…:定义初始化子类(cls):
…:如果不是hasattr(cls,“必选”):
…:引发未实现的错误
在[21]中:Y(X)类:
…:必需=5
...:     
In[22]:Y()
出[22]:

从3.3开始,我认为abc.abstractproperty已被弃用。

自从最初提出这个问题以来,python已经改变了抽象类的实现方式。我在Python3.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
这将强制派生类定义常量,否则在尝试实例化子类时将引发
TypeError
异常。当您想对抽象类中实现的任何功能使用常量时,必须通过
类型(self).常量来访问子类常量,而不仅仅是
常量,因为该值在基类中未定义

还有其他方法可以实现这一点,但我喜欢这种语法,因为在我看来,它对读者来说是最简单、最明显的

以前的答案都提到了有用的地方,但我觉得被接受的答案并不能直接回答这个问题,因为

  • 该问题要求在抽象类中实现,但公认的答案并不遵循抽象形式主义
  • 问题是实施是否有效。我认为,在这个答案中,强制执行更为严格,因为如果未定义
    CONSTANT
    ,则在实例化子类时会导致运行时错误。接受的答案允许实例化对象,并且仅在访问
    常量
    时抛出错误,从而降低了强制执行的严格性
这不是对原始答案的指责。自发布以来,抽象类语法发生了重大变化,在本例中允许更整洁、更功能化的实现

,您可以注释抽象类(或任何变量)的属性,而无需为该属性提供值

从abc导入abc
班长(ABC):
路径:str
类MyController(控制器):
path=“/home”

这使得代码非常干净,很明显属性是抽象的。从Python3.6开始,在if未被覆盖时尝试访问属性的代码将引发AttributeError,您可以使用\uuu init\u subclass\uuuuu在初始化时检查子类的类变量:

从abc导入abc
A类(ABC):
@类方法
定义初始子类(cls):
必需的\u类\u变量=[
“福”,
“酒吧”
]
对于所需类变量中的var:
如果不是hasattr(cls,var):
引发未实现的错误(
f'Class{cls}缺少必需的{var}`Class属性'
)

如果未定义缺少的类变量,则初始化子类时会出现错误,因此您不必等到访问缺少的类变量。

对于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
    """
尽管已经给出了一些很好的答案,但我认为这个答案仍然会增加一些价值。这种方法有两个优点:

  • 抽象方法主体中的
    pass
    更可取。与
    pass
    不同,
    ..
    表示无操作,其中
    pass
    仅表示没有实际实现

  • 建议使用
    而不是抛出
    未实现错误(…)
    。如果子类中缺少抽象字段的实现,则会自动提示极其详细的错误。相反,
    NotImplementedError
    本身并没有说明为什么缺少实现。此外,它需要人工来实际提高它

  • 我只修改了一点答案,这样所有的装饰师就不会占据太多的位置。如果你有多个这样的文章
    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
        """
    
    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