2017-05-30 94 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機制的工作,所以它應該可能表現出更少的意外,並且這意味着默認情況下不應該實例化。