我知道可以检查一个类是否实现了另一个类(如果它们确实相关)。
class A(ABC):
@abstractmethod
@property
def name(self):
raise NotImplementedException()
class B(A):
@property
def name(self):
return "B"
implements(B(), A) # Not a real method, what i'd like to exist
Run Code Online (Sandbox Code Playgroud)
但我还想检查一个不相关的类是否根据类的内容实现了另一个类。
Class C:
@property
def name(self):
return "C"
implements(C(), A) # Not a real method, what i'd like to exist
Run Code Online (Sandbox Code Playgroud)
有没有一种简单的方法可以做到这一点,或者我是否需要编写一个自定义函数来比较所有方法及其方法签名?
为什么不使用协议
并让runtime_checkable装饰器启用扩展检查
from typing import Protocol, runtime_checkable
@runtime_checkable
class A(Protocol):
@property
def name(self):
...
def implements(proto: Type):
""" Creates a decorator for classes that checks that the decorated class implements the runtime protocol `proto`
"""
def _deco(cls_def):
try:
assert issubclass(cls_def, proto)
except AssertionError as e:
e.args = (f"{cls_def} does not implement protocol {proto}",)
raise
return cls_def
return _deco
@implements(A)
class C:
def name(self):
return "C"
Run Code Online (Sandbox Code Playgroud)
根据需要添加所有额外的类型提示,以确保满足参数和方法返回值的约束。
注意:我还没有尝试过属性
有一种方法可以使用issubclass()orisinstance()而不是 来完成类似的事情implements()。这是通过实现一个新BaseInterface类来实现的,您可以使用它来派生适当的接口(即定义需要实现的方法列表的类型,但不能提供默认实现或实例化)。
BaseInterface首先,我们需要一个用于类接口类型的新元类。issubclass()这将提供和的所需行为isinstance(),并防止Interfaces 被错误地实例化。在代码片段的底部有一个空BaseInterface类,可以从中派生自定义接口。
class InterfaceMeta(type):
"""A metaclass for defining interfaces.
An interface defines a set of public methods and attributes needed
to interact with an object in a particular way, but is never
instantiated directly.
"""
def __new__(meta, name, bases, attrs):
"""Prevent interfaces from overriding __new__, __init__, or __call__."""
banned_methods = ('__new__', '__init__', '__call__')
if any([banned in attrs for banned in banned_methods]):
# Someone has tried to make it possible to instantiate an Interface
raise AttributeError(
'It is illegal to instantiate an Interface. '
'Therefore, it is illegal to implement any of the '
'following methods: {}'.format(', '.join(banned_methods))
)
else:
return super().__new__(meta, name, bases, attrs)
def __call__(cls, *args, **kwargs):
"""Prevent interfaces from being instantiated."""
raise NotImplementedError("Interface cannot be instantiated.")
def __instancecheck__(cls, instance):
"""Control the behaviour of `isinstance`.
An object is considered to be an instance of `cls` if it has
all of `cls`'s public attributes and methods.
"""
for cls_attr in dir(cls):
if cls_attr.startswith('_'):
# Private and protected attributes are not considered
# to be part of the interface.
continue
if not hasattr(instance, cls_attr):
# If `instance` is missing a public attribute of `cls`,
# it is not an instance of `cls`.
return False
elif (
callable(getattr(cls, cls_attr))
and not callable(getattr(instance, cls_attr))
):
# If `instance` is missing a public method of `cls`,
# it is not an instance of `cls`.
return False
return True
class BaseInterface(metaclass=InterfaceMeta):
"""Base class for all interfaces."""
pass
Run Code Online (Sandbox Code Playgroud)
现在我们可以通过继承来定义自定义接口BaseInterface。
class SomeInterface(BaseInterface):
"""A class defining an interface.
Other objects that have the same public methods and attributes
as `SomeInterface` are considered to be instances of `SomeInterface`.
"""
def name(self):
pass
def age(self):
pass
class Foo:
def name(self):
# Some implementation...
pass
def age(self):
# Some implementation...
pass
Run Code Online (Sandbox Code Playgroud)
由于Foo实现了 的所有公共方法SomeInterface,我们得到以下行为。
In [2]: issubclass(Foo(), SomeInterface)
Out[2]: True
In [3]: isinstance(Foo(), SomeInterface)
Out[3]: True
Run Code Online (Sandbox Code Playgroud)
更多详细信息请参见此处。
| 归档时间: |
|
| 查看次数: |
3869 次 |
| 最近记录: |