在Java中,我們可以通過將類設爲抽象類來阻止實例化類。我以爲python會以同樣的方式行事。但讓我吃驚,我發現,我可以創造一個抽象類的一個對象:防止抽象類實例化
from abc import ABCMeta
class Foo(metaclass=ABCMeta):
pass
Foo()
爲什麼蟒蛇允許這一點,我怎樣才能避免這種情況?
在Java中,我們可以通過將類設爲抽象類來阻止實例化類。我以爲python會以同樣的方式行事。但讓我吃驚,我發現,我可以創造一個抽象類的一個對象:防止抽象類實例化
from abc import ABCMeta
class Foo(metaclass=ABCMeta):
pass
Foo()
爲什麼蟒蛇允許這一點,我怎樣才能避免這種情況?
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機制的工作,所以它應該可能表現出更少的意外,並且這意味着默認情況下不應該實例化。
假設抽象類也會有一些'@ abstractmethod';如果是這樣,那*表示阻止實例化。所以在現實中應該基本上不是問題。 – deceze
Python很有趣。抽象方法可以包含代碼,也可以使用super來調用。那麼,爲什麼'抽象':) – codingsplash
這是一個相當抽象的概念在Python ...:P – deceze