Gro*_*Man 3 python abstract-class static-methods abc
这是我想要的设置: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'
Run Code Online (Sandbox Code Playgroud)
这里使用通常的Python约定定义A2和B2,并使用本答案中建议的方式定义A和B. 以下是我尝试的一些操作以及不希望的结果.
A/B班:
>>> B().f()
f
#This should have thrown, since B doesn't implement a static f()
Run Code Online (Sandbox Code Playgroud)
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()
Run Code Online (Sandbox Code Playgroud)
由于这些方法都没有给我我想要的输出,我如何实现我想要的?
你不能只做你想做的事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>
Run Code Online (Sandbox Code Playgroud)
我能够构建Concrete()即使两者foo并bar是简单的属性.
在ABCMeta元类只跟踪多少对象只剩下__isabstractmethod__属性为真; 当从元类(ABCMeta.__new__被调用)创建一个类时,该cls.__abstractmethods__属性然后被设置为一个frozenset具有所有仍然是抽象的名称的对象.
type.__new__然后测试frozenset并抛出一个TypeError如果你尝试创建一个实例.
你必须在这里制作自己的 __new__方法; 子类ABCMeta并在新__new__方法中添加类型检查.该方法应查找__abstractmethods__基类上的集合__isabstractmethod__,在MRO中查找具有该属性的相应对象,然后对当前类属性进行类型检查.
这意味着您在定义类时抛出异常,而不是实例.为此,您需要__call__为ABCMeta子类添加一个方法,并根据您自己的__new__方法收集的有关哪些类型错误的信息抛出异常; 一个类似的两阶段过程,就像现在的做法ABCMeta和type.__new__做法一样.或者,更新__abstractmethods__类上的集合以添加已实现但具有错误类型的任何名称,并将其保留type.__new__以抛出异常.
以下实现采用最后的方法; __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
Run Code Online (Sandbox Code Playgroud)
使用此元类,您可以获得预期的输出:
>>> 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>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1767 次 |
| 最近记录: |