pro*_*s91 5 python metaclass class
我想创建一个定义特定接口的类,然后要求所有子类都符合该接口。例如我想定义一个类
class Interface:
def __init__(self, arg1):
pass
def foo(self, bar):
pass
Run Code Online (Sandbox Code Playgroud)
然后请放心,如果我持有任何a具有 type A( 的子类)的元素Interface,那么我可以称a.foo(2)它会起作用。
看起来这个问题几乎解决了问题,但在这种情况下,由子类来显式更改它的元类。
理想情况下,我正在寻找类似于 Rust 中的 Traits 和 Impls 的东西,我可以在其中指定特定的 Trait 以及该 Trait 需要定义的方法列表,然后我可以确信具有该 Trait 的任何对象都具有这些方法定义的。
有没有办法在Python中做到这一点?
因此,首先,要声明一个显而易见的事实 - Python 有一个内置机制来测试派生类中方法和属性是否存在- 它只是不检查它们的签名。
其次,一个值得一看的好包是zope.interface. 尽管有zope命名空间,它还是一个完整的独立包,允许使用非常简洁的方法来公开多个接口的对象,但仅在需要时 - 然后释放命名空间。它确实需要一些学习,直到人们习惯它,但它可以非常强大,并为大型项目提供非常好的模式。
它是为 Python 2 设计的,当时 Python 的功能比现在少得多 - 而且我认为它不会执行自动接口检查(必须手动调用一个方法来查明类是否兼容) - 但会自动执行此调用不过,这很容易。
第三,链接的接受答案如何强制子类的方法签名?几乎可以工作,并且只需进行一项更改就足够好了。该示例的问题在于,它硬编码了创建新类的调用type,并且不传递type.__new__有关元类本身的信息。更换线路:
return type(name, baseClasses, d)
Run Code Online (Sandbox Code Playgroud)
为了:
return super().__new__(cls, name, baseClasses, d)
Run Code Online (Sandbox Code Playgroud)
然后,使基类 - 定义所需方法的基类使用元类 - 它将被任何子类正常继承。(只需使用 Python 3 语法来指定元类)。
抱歉 - 该示例是 Python 2 - 它还需要更改另一行,我最好重新发布它:
from types import FunctionType
# from /sf/answers/1628044211/
class SignatureCheckerMeta(type):
def __new__(mcls, name, baseClasses, d):
#For each method in d, check to see if any base class already
#defined a method with that name. If so, make sure the
#signatures are the same.
for methodName in d:
f = d[methodName]
for baseClass in baseClasses:
try:
fBase = getattr(baseClass, methodName)
if not inspect.getargspec(f) == inspect.getargspec(fBase):
raise BadSignatureException(str(methodName))
except AttributeError:
#This method was not defined in this base class,
#So just go to the next base class.
continue
return super().__new__(mcls, name, baseClasses, d)
Run Code Online (Sandbox Code Playgroud)
经过审查,我发现其中没有机制来强制实际实现方法。即,如果派生类中存在同名方法,则强制执行其签名,但如果派生类中根本不存在该方法,则上面的代码将不会发现它(并且超类上的方法将被调用 - 这可能是一个理想的行为)。
第四 - 虽然这会起作用,但它可能有点粗糙 - 因为它执行重写任何超类中另一个方法的任何方法都必须符合其签名。甚至兼容的签名也会被破坏。ABCMeta也许建立在现有机制的基础上会很好@abstractmethod,因为这些机制已经适用于所有极端情况。但请注意,此示例基于上面的代码,并在类创建时检查签名,而 Python 中的抽象类机制使其在实例化类时进行检查。保持不变将使您能够使用大型类层次结构,这可能会在中间类中保留一些抽象方法,而只有最终的具体类必须实现所有方法。只需使用它而不是ABCMeta作为接口类的元类,并像往常一样标记要检查接口的方法@abstractmethod。
class M(ABCMeta):
def __init__(cls, name, bases, attrs):
errors = []
for base_cls in bases:
for meth_name in getattr(base_cls, "__abstractmethods__", ()):
orig_argspec = inspect.getfullargspec(getattr(base_cls, meth_name))
target_argspec = inspect.getfullargspec(getattr(cls, meth_name))
if orig_argspec != target_argspec:
errors.append(f"Abstract method {meth_name!r} not implemented with correct signature in {cls.__name__!r}. Expected {orig_argspec}.")
if errors:
raise TypeError("\n".join(errors))
super().__init__(name, bases, attrs)
Run Code Online (Sandbox Code Playgroud)
您可以遵循该pyspark模式,其中基类的方法执行(可选)参数有效性检查,然后调用子类的“非公共”方法,例如:
class Regressor():
def fit(self, X, y):
self._check_arguments(X, y)
self._fit(X, y)
def _check_arguments(self, X, y):
if True:
pass
else:
raise ValueError('Invalid arguments.')
class LinearRegressor(Regressor):
def _fit(self, X, y):
# code here
Run Code Online (Sandbox Code Playgroud)