0

例如,我想在运行时检查给定对象是否具有方法foo()bar().

我的研究系统是用 python 3.6 构建的,它是高度参数化的,可以/应该接受任何类型的对象作为其内置模块的替代品。这个功能非常有用,因为使用这个系统的许多不同的学生可以很容易地研究不同的行为,而无需更改我系统的源代码。

问题是,如果他们错误地构建了模块,他们可能只有在整个实验结束后(可能是几个小时)才会发现这一点。

我正在寻找一种方法来在运行时的早期阶段检查他们的输入模块是否与特定接口匹配。甚至在实例化之前验证它会更好(当输入只是类型,而不是实例时)。例如some_interface.verify(MyClass).

解决方案

我在互联网上看到了很多解决方案(例如this),但没有一个适合:

  1. 最常见的解决方案 ( try/catch) 只会在运行时失败,并且不适用于多守护程序系统,因为只有一个守护程序失败时很难关闭。

  2. 检查isinstance()不会验证任何内容。情况可能更糟,因为开发人员可能忘记实现一个函数并使用基类实现,这可能不适合其当前实现。

  3. 使用ABC(抽象基类)需要开发者继承基类。如果她/他没有这样做,实例化类时不会发出警告或错误。另一方面,如果开发人员确实实现了接口但没有继承自base,那么issubclass()将返回 False。

  4. 使用zope 接口是我的首选,但它有一些缺点:

    • 它要求开发人员明确提及它正在实现接口。未能指定这将导致错误,尽管实际实现是正确的。
    • 它无法在实例化之前验证模块。该implementedBy()方法只会检查声明的模块是否正在实现接口,但要实际验证它,您应该调用verifyObject()实际实例。
    • 它不支持自 python 3.5 以来添加的新类型功能

编辑:显然,zope 还支持通过调用隐式实现,verifyObject(YourInterface, obj, tentative=True)这不会强制开发人员将类显式定义为接口的实现者。

4

2 回答 2

2

在我看来,问题不是工具的问题。主要问题是即使支持某些接口,也没有人能确定该模块是否真的有效。我会做的是为模块创建一个测试并在初始化插件时运行它。测试不仅要验证类型和接口(isinstance等等hasattr只是任务的工具),而且(如果可能的话)模块功能的最小正确性。例如,执行一些不需要太多时间来完成和验证结果的基本任务会很好。如果插件在此类测试任务期间失败,则该插件无效。

于 2017-05-07T12:00:29.027 回答
0

最近的 PEP 终于部分解决了这个问题。 PEP-0544引入typing.Protocol了允许定义可以在运行时验证的接口。这目前可通过名为typing-extensionstyping的模块的非官方扩展获得。

例如,它可以按如下方式使用:

from typing_extensions import Protocol, runtime
from typing import Any

@runtime
class IMyProtocol(Protocol):
    member: int

    def foo(self, parameter1: Any, parameter2: Any) -> Any:
        pass

    def bar(self, parameter1: Any) -> Any:
        pass

然后,如果我们定义一个类,我们可以检查它是否遵循协议:

class MyClass:
    def __init__(self):
        self.member = 5

    def foo(self, a, b):
        return a,b

    def bar(self, c):
        return c

isinstance(MyClass(), IMyProtocol)  # Returns True

如果我们定义错误,它将返回 false:

class MyOtherClass:
    def __init__(self):
        self.member = 5

    def bar(self, c):
        return c

isinstance(MyOtherClass(), IMyProtocol)  # Returns False

该解决方案的缺点是它不验证方法的参数。并不是说实现具有正确数量的参数,也不是参数的类型。

于 2019-03-12T22:40:02.307 回答