Why doesn't the abc.ABCMeta abstract instantiation check work on derivatives of `list` and `dict`?

落花浮王杯 提交于 2019-12-03 22:04:35

The problem

If you have the next class:

from abc import ABC, abstractmethod
class Foo(list, ABC):
    @abstractmethod
    def yourmethod(self):
        pass

the problem is that and object of Foo can be created without any error because Foo.__new__(Foo) delegates the call directly to list.__new__(Foo) instead of ABC.__new__(Foo) (which is responsible of checking that all abstract methods are implemented in the class that is going to be instantiated)

We could implement __new__ on Foo and try to call ABC.__new__:

class Foo(list, ABC):
    def __new__(cls, *args, **kwargs):
        return ABC.__new__(cls)

    @abstractmethod
    def yourmethod(self):
        pass
Foo()

But he next error is raised:

TypeError: object.__new__(Foo) is not safe, use list.__new__()

This is due to ABC.__new__(Foo) invokes object.__new__(Foo) which seems that is not allowed when Foo inherits from list

A possible solution

You can add additional code on Foo.__new__ in order to check that all abstract methods in the class to be instantiated are implemented (basically do the job of ABC.__new__).

Something like this:

class Foo(list, ABC):
    def __new__(cls, *args, **kwargs):
        if hasattr(cls, '__abstractmethods__') and len(cls.__abstractmethods__) > 0:
            raise TypeError(f"Can't instantiate abstract class {cls.__name__} with abstract methods {', '.join(cls.__abstractmethods__)}")
        return super(Foo, cls).__new__(cls)


    @abstractmethod
    def yourmethod(self):
        return -1

Now Foo() raises an error. But the next code runs without any issue:

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