如何为python中存在的对象属性(属性)编写接口(契约)?

How to write interface (contract) for object property (attribute) existence in python?

我想就某些函数的参数签订合同,以强制执行参数对象必须具有特定的 属性。我知道 python 不是一种严格类型化的语言,但有时拥有契约和接口非常有用。 Python 现在有类型提示,这很棒,所以我们可以这样做:

def myfunc(myparam: MyType) -> SomeType:
    myparam.myprop # this should exist

但是我怎么能说 MyType 必须有一个特定的对象 属性 (myprop) 而无需在 运行 时间内插入断言和抛出异常? 我可以用 abc metaclasses 定义抽象 classes,它可以用作接口。

from abc import ABC, abstractmethod
class MyInterface(ABC):
     @property
     @abstractmethod
     def myprop(self) -> int: pass

现在在代码的某处我可以将 MyType 定义为:

class MyType(MyInterface):
    myprop = 8

有效,但是 myprop 是 class 属性 而不是 对象 属性(属性)。我当然可以这样做:

class MyType(MyInterface):
    myprop = 0
    def __init__(self):
        self.myprop = 8

很好,但我必须定义一个(不必要的)class ("static") 属性 并用对象 属性 有效地隐藏它。不是很干净。此外,现在我有一个 myprop 的默认值,这不是我想要的。但如果我这样做:

class MyType(MyInterface):
    myprop = None  # wrong type here
    def __init__(self):
        self.myprop = 8

这是错误的,因为 myprop 必须是 int 而不能是 None,它被 linter 正确捕获。应该有一个对象 属性 没有 class 属性。

目标是像 mypy 这样的静态检查器可以捕获 class 不遵守要求参数实例必须具有一些 属性 的已定义接口或合同的实现错误。 =17=]

实现此目的的 pythonic(或不是 pythonic)方法是什么?

您不需要创建新接口或实现 ABC。您可以使用 @dataclass。我在 pycharm 中使用默认代码检查器(警告在评论中)。

from dataclasses import dataclass


@dataclass
class MyType:
    my_prop: int


@dataclass
class SomeType:
    my_prop_out: int


def my_func_ok(my_param: MyType) -> SomeType:
    some_type = SomeType(my_prop_out=my_param.my_prop)
    return some_type


def my_func_bad(my_param: MyType) -> SomeType:
    return my_param              # this is not returning SomeType


my_type = MyType()               # this is expecting to set my_prop
my_type = MyType(my_prop="sss")  # this is expecting to set my_prop with int not str
my_func_ok(my_param=100)         # this is expecting MyType object

my_func_ok(my_param=MyType(my_prop=10))  # this is correct, no errors

我正在添加 pycharm 代码检查器警告的图片: