2017-05-30 96 views
2

在Java中,我们可以通过将类设为抽象类来阻止实例化类。我以为python会以同样的方式行事。但让我吃惊,我发现,我可以创造一个抽象类的一个对象:防止抽象类实例化

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

为什么蟒蛇允许这一点,我怎样才能避免这种情况?

+2

假设抽象类也会有一些'@ abstractmethod';如果是这样,那*表示阻止实例化。所以在现实中应该基本上不是问题。 – deceze

+0

Python很有趣。抽象方法可以包含代码,也可以使用super来调用。那么,为什么'抽象':) – codingsplash

+1

这是一个相当抽象的概念在Python ...:P – deceze

回答

0

Python是用于“成年人同意” - 如果你想(或模块成员资格),你可以通过在项目中命名约定来将类标记为抽象类。然而,做一个艰难的“不合理的”抽象类是可行的 - 但这并不会增加项目本身的安全性或良好实践,正如问题的提法者所提出的那样。因此,为了保留ABC抽象类的剩余机器,您可以继承ABCMeta类,并使用它来装饰__new__方法,以便它不会实例化 - 否则,只需执行相同的操作,但是可以从type继承。

换句话说,下面的代码包含了用它作为元类创建的类的方法。当该方法运行时,它会检查它正在实例化的类是否是用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() 

.... 

只是为了完整起见 - ABCMeta的Python中是否包含至少一个“abstractmethod”没有实例化。就像其他O.O.在更多静态语言中强制执行的功能,这个想法是按照惯例。但是,是的,我同意,由于他们完成了创建AbstractClass机制的工作,所以它应该可能表现出更少的意外,并且这意味着默认情况下不应该实例化。