gpt4 book ai didi

python - 在 Python 中,如何强制抽象方法在子类上是静态的?

转载 作者:太空宇宙 更新时间:2023-11-03 12:58:55 26 4
gpt4 key购买 nike

这是我想要的设置:A 应该是具有静态和抽象方法 f() 的抽象基类。 B应该继承自A。要求:1.你不应该能够实例化A2. 你不应该实例化 B,除非它实现了一个静态的 f()

this 中汲取灵感问题,我尝试了几种方法。使用这些定义:

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'

此处 A2 和 B2 是使用常用的 Python 约定定义的,A 和 B 是使用 this 中建议的方式定义的回答。以下是我尝试过的一些操作以及不想要的结果。

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 做你想做的事。 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>

即使 foobar 都是简单的属性,我也能够构建 Concrete()

ABCMeta 元类仅跟踪在 __isabstractmethod__ 属性为真时剩余的对象数量;当从元类创建一个类时(调用 ABCMeta.__new__),cls.__abstractmethods__ 属性然后被设置为一个 frozenset 对象,其中包含所有仍然是抽象的名称。

type.__new__ 然后测试该 frozenset 并在您尝试创建实例时抛出 TypeError

您必须在此处生成您的自己的 __new__ 方法;子类 ABCMeta 并在新的 __new__ 方法中添加类型检查。该方法应该在基类上查找 __abstractmethods__ 集,在 MRO 中找到具有 __isabstractmethod__ 属性的相应对象,然后对当前类属性进行类型检查。

这意味着您将在定义 时抛出异常,而不是实例。为此,您需要将 __call__ 方法添加到您的 ABCMeta 子类,并根据您自己的 __new__ 收集的信息抛出异常关于什么类型错误的方法;与 ABCMetatype.__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

使用这个元类,您可以获得预期的输出:

>>> 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>

关于python - 在 Python 中,如何强制抽象方法在子类上是静态的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29455660/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com