Python:联合类型和模式处理的静态类型检查

问题描述 投票:0回答:1

在 Ocaml/Haskell/... 等函数式语言中,我可以输入类似以下内容:

type expr =
    | Nb of float
    | Add of expr * expr
    | Soust of expr * expr
    | Mult of expr * expr
    | Div of expr * expr
    | Opp of expr

let rec eval x = match x with
    | Nb n -> n
    | Add (e1, e2) -> (eval e1) +. (eval e2)
    | Soust (e1, e2) -> (eval e1) -. (eval e2)
    | Mult (e1, e2) -> (eval e1) *. (eval e2)
    | Div (e1, e2) -> (eval e1) /. (eval e2)
    | Opp n -> -. (eval n)

一旦我的代码编译完毕,我将保证对于任何

x
类型的
expr
eval x
将始终产生
float
类型的输出。这明显意味着我的模式匹配不会忘记任何情况,因此如果后来我在
expr
类型中添加一个新项目,它将无法编译,直到我在模式匹配中添加这个新情况。

遗憾的是,我在 python 中找不到任何可以提供如此强有力保证的东西,包括 Python 10 的打字系统……我错过了什么吗?

python pattern-matching python-typing static-typing
1个回答
0
投票

我很惊讶地发现这个功能实际上是可能的! (至少使用python 3.11,之前没有尝试过)

这要归功于 https://peps.python.org/pep-0622/#exhaustiveness-checks,它提供了必须通过类型检查的

match
构造。

使用

$ python foo.py
执行以下代码,并使用
$ mypy --strict foo.py
运行类型检查。

演示:

### Define our types

# See https://github.com/python/mypy/issues/17139 to get a nicer
# syntax once the bug is solved

class Mult:
    # Expr is not yet defined, so we use forward references
    # https://peps.python.org/pep-0484/#forward-references
    left: 'Expr'
    right: 'Expr'
    def __init__(self, left:'Expr', right:'Expr'):
        self.left = left
        self.right = right

class Add:
    # Expr is not yet defined, so we use forward references
    # https://peps.python.org/pep-0484/#forward-references
    left: 'Expr'
    right: 'Expr'
    def __init__(self, left:'Expr', right:'Expr'):
        self.left = left
        self.right = right

class Const:
    val: int
    def __init__(self, val:int):
        self.val = val

Expr = Const | Add | Mult

### Define our functions

def my_eval(e : Expr) -> int:
    match e:
        case Const():
            return e.val
        case Add():
            return my_eval(e.left) * my_eval(e.right)
        case Mult():
            return my_eval(e.left) + my_eval(e.right)

### Use them
        
print(my_eval(Const(42)))
print(my_eval(Add(Const(42),Const(45))))
$ python foo.py
42
1890
$ mypy --strict foo.py
Success: no issues found in 1 source file

如果您现在决定删除一项,例如通过评论

Add()
案例,您会收到错误(我必须承认,不是很清楚):

$ mypy --strict foo2.py
foo2.py:37: error: Missing return statement  [return]
Found 1 error in 1 file (checked 1 source file)

待办事项 找到一个更简单的接口来定义类型会很棒,特别是我发现

class Mult(tuple[int, int]):
    pass

但到目前为止它似乎在内部不起作用

mypy
https://github.com/python/mypy/issues/17139

© www.soinside.com 2019 - 2024. All rights reserved.