Python中没有抽象方法的抽象数据类:禁止实例化

Hli*_*bii 5 python oop abc python-3.x python-dataclasses

即使一个类是从 继承的ABC,看起来它仍然可以被实例化,除非它包含抽象方法。

有了下面的代码,防止Identifier创建对象的最佳方法是什么:Identifier(['get', 'Name'])

from abc import ABC
from typing import List
from dataclasses import dataclass

@dataclass
class Identifier(ABC):
    sub_tokens: List[str]

    @staticmethod
    def from_sub_tokens(sub_tokens):
        return SimpleIdentifier(sub_tokens) if len(sub_tokens) == 1 else CompoundIdentifier(sub_tokens)


@dataclass
class SimpleIdentifier(Identifier):
    pass


@dataclass
class CompoundIdentifier(Identifier):
    pass
Run Code Online (Sandbox Code Playgroud)

如果这个问题已经得到回答,我提前道歉。这看起来很基本,但是,由于某种原因我找不到答案。

干杯,赫利卜。

Hli*_*bii 13

我发现的最简单的方法是检查方法中对象的类型__post_init__

@dataclass
class Identifier(ABC):
    ...

    def __post_init__(self):
        if self.__class__ == Identifier:
            raise TypeError("Cannot instantiate abstract class.")

    ...
Run Code Online (Sandbox Code Playgroud)


Jun*_*ius 7

您可以创建一个AbstractDataclass类来保证这种行为,并且每次遇到您所描述的情况时都可以使用它。

@dataclass 
class AbstractDataclass(ABC): 
    def __new__(cls, *args, **kwargs): 
        if cls == AbstractDataclass or cls.__bases__[0] == AbstractDataclass: 
            raise TypeError("Cannot instantiate abstract class.") 
        return super().__new__(cls)
Run Code Online (Sandbox Code Playgroud)

所以,如果Identifier继承自AbstractDataclass而不是ABC直接从,修改__post_init__将是不需要的。

@dataclass
class Identifier(AbstractDataclass):
    sub_tokens: List[str]

    @staticmethod
    def from_sub_tokens(sub_tokens):
        return SimpleIdentifier(sub_tokens) if len(sub_tokens) == 1 else CompoundIdentifier(sub_tokens)


@dataclass
class SimpleIdentifier(Identifier):
    pass


@dataclass
class CompoundIdentifier(Identifier):
    pass
Run Code Online (Sandbox Code Playgroud)

实例化Identifier会引发TypeError但不会实例化SimpleIdentifierCompountIdentifier。并且AbstractDataclass可以在代码的其他部分重用。