abstract classes without abstract methods creating objects in python

邮差的信 提交于 2019-12-23 08:11:40

问题


Basically, I knew that abstract base classes are used as skeleton classes just like regular classes, but enforces that abstract methods should be overridden by the child/inherited classes if it has one like below

Class AbstractClass(object):
    __metaclass__ = abc.ABCMeta

    @abstractmethod
    def absmethod(self):
        pass

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

obj = ChildClass()

So we can create an object of ChildClass as above

We know we can't instantiate an abstract class as it meant to be just skeleton and we will get an error if we try to instantiate it as below

obj = AbstractClass()

*** TypeError: Can't instantiate abstract class AbstractClass with abstract methods absmethod

But what my actual query about posting this StackOverflow is if we create an abstract class by using abc.ABCMeta, without abstract methods, I can able to create an instance of the abstract class which should not be the case(correct me if I am wrong)

Class AbstractClass(object):
    __metaclass__ = abc.ABCMeta

obj = AbstractClass()

OOOPPPSS it worked, we can actually create an object of abstract classes without abstract methods? So please let me know the key points behind this


回答1:


Since Python is a dynamic languages, the very idea of enforcing classes to inherit from a particular class goes against duck typing. Hence, the use case of Abstract classes in Python is pretty limited and provided more for a conventional reason. Still if you want to block the instantiation of a class without declaring virtual methods, you can, however,

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)



回答2:


From the docs:

A class that has a metaclass derived from ABCMeta cannot be instantiated unless all of its abstract methods and properties are overridden.

Conversely, this means that any class with no abstract methods or properties like your AbstractClass can be instantiated.


If you want to disallow instantiation of the topmost parent class, you can write a custom class that performs a type check in its __new__ method:

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)
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"

You can also write a __new__ method that prevents instantiation of classes with no abstract methods:

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



回答3:


Here is solution without using ABC. More on specific case it required for here

class AbstractClass:
    _is_abstract = True

    def __init__(self):
        if self._is_abstract:
            raise RuntimeError("Abstract class instantiation.")
        # do initialization

    def __init_subclass__(self):   # is called every time class is subclassed
        self._is_abstract = False  # thus makes sure abstract check returns False on subclass


来源:https://stackoverflow.com/questions/50099600/abstract-classes-without-abstract-methods-creating-objects-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!