【发布时间】:2019-10-22 21:53:52
【问题描述】:
我想对某个函数的参数进行约定,以强制参数对象必须具有特定属性。我知道 python 不是一种严格类型的语言,但有时拥有合同和接口非常有用。 Python 现在有类型提示,这很棒,所以我们可以这样做:
def myfunc(myparam: MyType) -> SomeType:
myparam.myprop # this should exist
但是我怎么能说 MyType 必须有一个特定的对象属性(myprop)而不在运行时插入断言和抛出异常呢? 我可以用 abc 元类定义抽象类,它可以用作接口。
from abc import ABC, abstractmethod
class MyInterface(ABC):
@property
@abstractmethod
def myprop(self) -> int: pass
现在我可以在代码中的某个地方将 MyType 定义为:
class MyType(MyInterface):
myprop = 8
它正在工作,但是 myprop 是一个类属性,而不是一个对象属性(属性)。我当然可以这样做:
class MyType(MyInterface):
myprop = 0
def __init__(self):
self.myprop = 8
很好,但是我必须定义一个(不必要的)类(“静态”)属性,并使用对象属性有效地隐藏它。不是很干净。此外,现在我有一个 myprop 的默认值,这不是我想要的。但如果我这样做:
class MyType(MyInterface):
myprop = None # wrong type here
def __init__(self):
self.myprop = 8
这是错误的,因为 myprop 必须是 int 并且不能是 None,这会被 linter 正确捕获。应该有一个没有类属性的对象属性。
目标是像 mypy 这样的静态检查器可以捕获实现错误,其中类不遵守定义的接口或要求参数实例必须具有某些属性的协定。
实现这一点的 Pythonic(或不是 Pythonic)方法是什么?
【问题讨论】:
-
让我想起了这个stackoverflow.com/a/50381071 - 但我的自定义解决方案可能无法与您的 linter 配合使用。最后一个例子 - 但类型注释对我来说似乎最好。顺便提一句。 “属性”可能是比“对象属性”更好的关键字。
-
术语“属性”添加以澄清。您的解决方案可以满足我的需要,我会看看 linter 是否也喜欢它。鸭子打字很时髦,但在某个地方我应该能够描述什么是鸭子。
-
我对这种混合打字和 abc 术语感到有些困惑。您在寻找 静态类型检查 断言还是 abc 运行时 断言?并且您需要一些抽象/协议
MyType来保证每个实现 class 都有一个mypropattribute? -
@MisterMiyagi ABC 绝对是关于打字的。这里没有混合。我想定义一个类型安全的接口(或者最好是一个合同)。我想静态地强制执行一些关于对象参数的规则,比如“必须有一个特定的属性”。我肯定对运行时断言不感兴趣,这可以很简单地完成。当然,这在纯 python 中是不可能的,但在 linter 中可能是可能的。
-
好吧,让我们专注于你到底需要什么。 “属性”是指具体的
property还是一般的任何属性?你真的想要一个“类属性”还是只是你非工作尝试的一部分?您是否需要类型检查器来验证每个实现class MyType(MyInterface)、MyType是否满足MyInterface,或者您只是希望能够定义一个可以在需要MyInterface的任何地方安全使用的class MyType?
标签: python python-3.x interface abstract-class contract