代码之家  ›  专栏  ›  技术社区  ›  Adrian

使用返回类型声明处理Python类型检查器错误

  •  0
  • Adrian  · 技术社区  · 1 年前

    我在使用Pyright类型检查器的Python代码中遇到了一个问题,特别是当我试图从返回类型声明中删除Any时。

    先看/读这个问题: Handling Python Type Checker Errors with Return Type Declarations (fields default values = None)

    错误消息:

    Function with declared return type "int" must return value on all code paths. "None" is incompatible with "int".
    

    这个案子怎么样?尽管我的类不包含任何None,但我仍然会得到与上面相同的错误(堆栈溢出的问题)。

    from dataclasses import dataclass
    from typing import Any, Union, List
    
    class Exp:
        pass
    
    @dataclass
    class Const(Exp):
        value: int
    
    @dataclass
    class Negate(Exp):
        value: Exp
    
    @dataclass 
    class Add(Exp):
        value1: Exp
        value2: Exp
    
    @dataclass
    class Multiply(Exp):
        value1: Exp
        value2: Exp
    
    
    def eval_exp_old(e: Exp) -> int | Any:
        match e:
            case Const(i):
                return i
            case Negate(e2):
                return -eval_exp_old(e2)
            case Add(e1, e2):
                return eval_exp_old(e1) + eval_exp_old(e2)
            case Multiply(e1, e2):
                return eval_exp_old(e1) * eval_exp_old(e2)
            
    
    test_exp: Exp = Multiply(Negate(Add(Const(2), Const(2))), Const(7))
    # -28
    old_test = eval_exp_old(test_exp)
    

    错误

    def eval_exp_old(e: Exp) -> int { ... }
    

    我很感激你的建议。

    顺便说一句,看看这篇文章,也贴出了被接受的答案。 https://discuss.python.org/t/draft-pep-sealed-decorator-for-static-typing/49206

    1 回复  |  直到 1 年前
        1
  •  1
  •   InSync Christian C. Salvadó    1 年前

    如果有人继承自 Exp 并将其作为值传递给 eval_exp_old() ?

    class NewExp(Exp): pass
    
    eval_exp_old(NewExp())  # Pass type checking, yet silently returns None
    

    在匹配树中添加一个默认案例,并在其中抛出一个错误。这样,你会得到一个运行时错误(最好是在测试中),告诉你忘记处理某些案例:

    match e:
        case Const(i):
            return i
        # ...
        case _:
            raise ValueError(f'Value {e} of type {type(e).__name__} is not expected')
    

    一般来说,您想要的是所谓的“密封类”:

    @sealed  # This means all subclasses are known ahead of time
    class Exp:
        pass
    

    这只存在于 a recent PEP draft this relevant thread 在Python官方论坛上。