没有抽象方法的抽象类在python中创建对象

Shi*_*dla 7 python abstract-class

基本上,我知道抽象基类像常规类一样用作框架类,但强制要求子/继承类覆盖抽象方法,如果它有下面的类

Class AbstractClass(object):
    __metaclass__ = abc.ABCMeta

    @abstractmethod
    def absmethod(self):
        pass

class ChildClass(AbstractClass):
    def absmethod(self):
        print "Yeah!"

obj = ChildClass()
Run Code Online (Sandbox Code Playgroud)

所以我们可以创建一个ChildClass如上所述的对象

我们知道我们不能实例化一个抽象类,因为它只是骨架,如果我们试图将它实例化,我们将得到一个错误

obj = AbstractClass()

*** TypeError: Can't instantiate abstract class AbstractClass with abstract methods absmethod
Run Code Online (Sandbox Code Playgroud)

但是我对发布这个的实际查询StackOverflow是,如果我们通过使用abc.ABCMeta而不使用抽象方法创建一个抽象类,我可以创建抽象类的实例,但不应该是这种情况(如果我错了,请纠正我)

Class AbstractClass(object):
    __metaclass__ = abc.ABCMeta

obj = AbstractClass()
Run Code Online (Sandbox Code Playgroud)

OOOPPPSS它有效,我们实际上可以创建一个抽象类的对象,而无需抽象方法?所以请让我知道这背后的关键点

Ara*_*Fey 8

文档

ABCMeta除非重写所有抽象方法和属性,否则无法实例化具有派生自元类的类。

相反,这意味着任何没有抽象方法或属性的类AbstractClass 可以实例化。


如果你想禁止最顶层父类的实例化,你可以编写一个在其__new__方法中执行类型检查的自定义类:

class SubclassOnlyABC(object):
    __metaclass__ = abc.ABCMeta

    def __new__(cls, *args, **kwargs):
        if cls.__bases__ == (SubclassOnlyABC,):
            msg = 'Abstract class {} cannot be instantiated'.format(cls.__name__)
            raise TypeError(msg)

        return super(SubclassOnlyABC, cls).__new__(cls, *args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
class AbstractClass(SubclassOnlyABC):
    pass

class ChildClass(AbstractClass):
    pass

ChildClass()  # works because it's a child class of an abstract class
AbstractClass()  # throws TypeError because its parent class is "object"
Run Code Online (Sandbox Code Playgroud)

您还可以编写一个__new__方法来阻止没有抽象方法的类的实例化:

class NonEmptyABC(object):
    __metaclass__ = abc.ABCMeta

    def __new__(cls, *args, **kwargs):
        # check if ANY abstractmethod exists
        for parentcls in cls.__mro__:
            if any(getattr(attr, '__isabstractmethod__', False)
                               for attr in vars(parentcls).values()):
                break
        else:
            msg = 'Abstract class {} cannot be instantiated'.format(cls.__name__)
            raise TypeError(msg)

        return super(NonEmptyABC, cls).__new__(cls, *args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
class EmptyAbstractClass(NonEmptyABC):
    pass

class NonemptyAbstractClass(NonEmptyABC):
    @abc.abstractmethod
    def foo(self):
        pass

class NonemptyChild(NonemptyAbstractClass):
    def foo(self):
        pass

NonemptyChild()  # works because "foo" is an abstractmethod
EmptyAbstractClass()  # throws TypeError because there are no abstractmethods
Run Code Online (Sandbox Code Playgroud)


hsp*_*her 7

由于 Python 是一种动态语言,因此强制类从特定类继承的想法本身就违背了鸭子类型。因此,Python 中抽象类的用例非常有限,并且出于传统原因提供了更多。不过,如果您想在不声明虚拟方法的情况下阻止类的实例化,您可以:

class AbstractClass(object):

    __metaclass__ = abc.ABCMeta

    def __new__(cls, *args, **kwargs):
        if cls is AbstractClass:
            raise Exception('Abstract class cannot be instantiatied')

        return object.__new__(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)