防止实例化抽象类

时间:2017-05-30 08:00:19

标签: python-3.x abstract-class metaclass

在Java中,我们可以通过使类成为抽象类来阻止类的实例化。我以为python会表现得一样。但令我惊讶的是,我发现我可以创建一个抽象类的对象:

from abc import ABCMeta
class Foo(metaclass=ABCMeta):
    pass
Foo()

为什么python允许这样做?如何防止这种情况?

1 个答案:

答案 0 :(得分:1)

Python用于"同意成年人" - 如果需要(或模块成员资格),可以通过在项目中命名约定来将类标记为抽象。但是,做一个难以解决的问题是可行的。抽象类 - 但这不会增加项目本身的安全性或良好的实践,正如问题的提交者提出的那样。

因此,为了保留ABC的抽象类的剩余机制,你可以继承ABCMeta类,并使用它来装饰__new__方法,这样它就不会实例化 - 否则,只是做同样的事,但继承自type

换句话说,下面的代码将__new__方法包装在用它创建的类作为元类上。当运行该方法时,它会检查它实例化的类是否是标记有ABC元本身的类,如果是,则会引发类型错误。

class ReallyAbstract(ABCMeta):
    def __new__(metacls, name, bases, namespace):
        outter_cls = super().__new__(metacls, name, bases, namespace)
        for bases in outter_cls.__mro__:
            if getattr(getattr(bases, "__new__", None), "_abstract", False):
                # Base class already marked as abstract. No need to do anything else
                return outter_cls
        original_new = getattr(outter_cls, "__new__")
        if getattr(original_new, "_abstract", False):
            # If we get a method that has already been wrapped
            # just return it unchanged.
            # TODO: if further classes on the hierarhy redfine __new__
            return outter_cls

        def __new__(cls, *args, **kw):
            if cls is outter_cls:
                raise TypeError
            return original_new(cls, *args, **kw)
        __new__._abstract = True
        outter_cls.__new__ = __new__
        return outter_cls

在控制台上:

In [7]: class A(metaclass=ReallyAbstract):
    ...:     pass
    ...: 

In [7]: A()
   TypeError                                 Traceback (most recent call last)
<ipython-input-7-...> in <module>()
----> 1 A()

....

仅仅为了完整性 - 如果Python中的ABCMeta包含至少一个&#34; abstractmethod&#34;则它不可实例化。就像其他O.O.在更多静态语言中强制执行的功能,其想法是遵循惯例。但是,是的,我同意,因为他们完成了创建一个AbstractClass机制的工作,它应该表现得更少的意外,这意味着默认情况下不应该是可实例化的。