![](/img/trans.png)
[英]How to implement abstract class methods as static method in inherited class in python?
[英]In Python, how to enforce an abstract method to be static on the child class?
這是我想要的設置:A應該是一個帶有靜態和抽象方法f()的抽象基類。 B應該從A繼承。要求:1。你不應該能夠實例化A 2.你不應該實例化B,除非它實現了靜態的f()
從這個問題中汲取靈感,我嘗試了幾種方法。 有了這些定義:
class abstractstatic(staticmethod):
__slots__ = ()
def __init__(self, function):
super(abstractstatic, self).__init__(function)
function.__isabstractmethod__ = True
__isabstractmethod__ = True
class A:
__metaclass__ = abc.ABCMeta
@abstractstatic
def f():
pass
class B(A):
def f(self):
print 'f'
class A2:
__metaclass__ = abc.ABCMeta
@staticmethod
@abc.abstractmethod
def f():
pass
class B2(A2):
def f(self):
print 'f'
這里使用通常的Python約定定義A2和B2,並使用本答案中建議的方式定義A和B. 以下是我嘗試的一些操作以及不希望的結果。
A / B班:
>>> B().f()
f
#This should have thrown, since B doesn't implement a static f()
A2 / B2類:
>>> A2()
<__main__.A2 object at 0x105beea90>
#This should have thrown since A2 should be an uninstantiable abstract class
>>> B2().f()
f
#This should have thrown, since B2 doesn't implement a static f()
由於這些方法都沒有給我我想要的輸出,我如何實現我想要的?
只用ABCMeta
你不能做你想做的ABCMeta
。 ABC強制執行不進行任何類型檢查,只強制存在具有正確名稱的屬性 。
舉個例子:
>>> from abc import ABCMeta, abstractmethod, abstractproperty
>>> class Abstract(object):
... __metaclass__ = ABCMeta
... @abstractmethod
... def foo(self): pass
... @abstractproperty
... def bar(self): pass
...
>>> class Concrete(Abstract):
... foo = 'bar'
... bar = 'baz'
...
>>> Concrete()
<__main__.Concrete object at 0x104b4df90>
我能夠構造Concrete()
即使foo
和bar
都是簡單的屬性。
ABCMeta
元類僅跟蹤__isabstractmethod__
屬性為真的剩余對象數量; 當從元類創建一個類( ABCMeta.__new__
被調用)時,然后將cls.__abstractmethods__
屬性設置為一個frozenset
對象,其中所有名稱仍然是抽象的。
type.__new__
然后測試該frozenset
並在嘗試創建實例時拋出TypeError
。
你必須在這里制作自己的 __new__
方法; 子類ABCMeta
並在新的__new__
方法中添加類型檢查。 該方法應該在基類上查找__abstractmethods__
集,在MRO中找到具有__isabstractmethod__
屬性的相應對象,然后對當前類屬性進行類型檢查。
這意味着您在定義類時拋出異常,而不是實例。 為了實現這一點,你需要為你的ABCMeta
子類添加一個__call__
方法,並根據你自己的__new__
方法收集的關於哪些類型錯誤的信息拋出異常; 類似於ABCMeta
和type.__new__
做的兩個階段的過程。 或者,更新類上的__abstractmethods__
設置以添加已實現但具有錯誤類型的任何名稱,並使其type.__new__
以拋出異常。
以下實現采用最后的方法; 如果實現的類型不匹配(使用映射), __abstractmethods__
名稱添加回__abstractmethods__
:
from types import FunctionType
class ABCMetaTypeCheck(ABCMeta):
_typemap = { # map abstract type to expected implementation type
abstractproperty: property,
abstractstatic: staticmethod,
# abstractmethods return function objects
FunctionType: FunctionType,
}
def __new__(mcls, name, bases, namespace):
cls = super(ABCMetaTypeCheck, mcls).__new__(mcls, name, bases, namespace)
wrong_type = set()
seen = set()
abstractmethods = cls.__abstractmethods__
for base in bases:
for name in getattr(base, "__abstractmethods__", set()):
if name in seen or name in abstractmethods:
continue # still abstract or later overridden
value = base.__dict__.get(name) # bypass descriptors
if getattr(value, "__isabstractmethod__", False):
seen.add(name)
expected = mcls._typemap[type(value)]
if not isinstance(namespace[name], expected):
wrong_type.add(name)
if wrong_type:
cls.__abstractmethods__ = abstractmethods | frozenset(wrong_type)
return cls
使用此元類,您可以獲得預期的輸出:
>>> class Abstract(object):
... __metaclass__ = ABCMetaTypeCheck
... @abstractmethod
... def foo(self): pass
... @abstractproperty
... def bar(self): pass
... @abstractstatic
... def baz(): pass
...
>>> class ConcreteWrong(Abstract):
... foo = 'bar'
... bar = 'baz'
... baz = 'spam'
...
>>> ConcreteWrong()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class ConcreteWrong with abstract methods bar, baz, foo
>>>
>>> class ConcreteCorrect(Abstract):
... def foo(self): return 'bar'
... @property
... def bar(self): return 'baz'
... @staticmethod
... def baz(): return 'spam'
...
>>> ConcreteCorrect()
<__main__.ConcreteCorrect object at 0x104ce1d10>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.