Python 类型提示条件变量应用程序

Python 类型提示条件变量应用程序,python,pycharm,type-hinting,mypy,Python,Pycharm,Type Hinting,Mypy,我试图键入一个构造函数的部分应用程序,一旦提供了一个“tag”实例,它就会被完全应用。这是通过包装类实现的,包装类存储构造函数和任何部分应用的参数。由于包装器适用于多种类型,因此必须采用可变的*args 这就产生了两种情况,它们的论点不同: 应用:(标记:标记,…)->Cls 存储:(…)->部分[Cls] 值得注意的是,2。案例可能会也可能不会收到第一个参数。两者在算术上重叠,因为它们是可变的。这很容易实现。我已尝试使用@overload键入提示: from typing import Typ

我试图键入一个构造函数的部分应用程序,一旦提供了一个“tag”实例,它就会被完全应用。这是通过包装类实现的,包装类存储构造函数和任何部分应用的参数。由于包装器适用于多种类型,因此必须采用可变的
*args

这就产生了两种情况,它们的论点不同:

  • 应用:
    (标记:标记,…)->Cls
  • 存储:
    (…)->部分[Cls]
  • 值得注意的是,2。案例可能会也可能不会收到第一个参数。两者在算术上重叠,因为它们是可变的。这很容易实现。我已尝试使用
    @overload
    键入提示:

    from typing import TypeVar, Generic
    
    #: the class to partially construct
    Cls = TypeVar('Cls')
    
    class Tag:
        """Instances of this class complete the partial application"""
    
    class Partial(Generic[Cls]):
        """Partially construct ``ctor`` until a :py:class:`~.Tag` is applied"""
        def __init__(self, ctor: Type[Cls], *args):
            self.ctor = ctor
            self.args = args
    
        # type hints
        @overload
        def __call__(self, tag: Tag, *args) -> Cls:
            ...
    
        @overload
        def __call__(self, *args) -> 'Partial[Cls]':
            ...
    
        # implementation
        def __call__(self, *args):
            if args and isinstance(args[0], Tag):
                return self.ctor(args[0], *self.args, *args[1:])
            return Partial(self.ctor, *self.args, *args)
    
    然而,mypy和PyCharm对此都不满意(PyCharm目前需要显式方法调用,但这不是我的问题)。使用显式非标记扩展第二个重载
    (标记:Any,…)->Partial[Cls]
    无法解决此问题。这两种工具要么报告类型不匹配、不兼容的重载,要么返回到
    Any
    Union

    此场景的正确注释是什么


    类型检查代码示例:

    class VariadicString(str):
        def __new__(cls, *args):
            return str(args)
    
    
    a = RecursivePartial(VariadicString, 1, 2, 3)
    b = a(4, 5, 6)
    c = b(Tag(), 7, 8, 9)
    reveal_locals()  # absent for PyCharm
    
    mypy
    正确识别
    a
    b
    c
    的类型,但由于不兼容的重载重叠而拒绝程序:

    test.py:17: error: Overloaded function signatures 1 and 2 overlap with incompatible return types
    test.py:38: error: Revealed local types are:
    test.py:38: error: a: test.Partial[test.VariadicString*]
    test.py:38: error: b: test,Partial[test.VariadicString*]
    test.py:38: error: c: test.VariadicString*
    
    PyCharm不拒绝该程序,但将
    c
    错误识别为两种返回类型的
    Union

    a: Partial[VariadicString]
    b: Partial[VariadicString]
    c: Union[VariadicString, Partial[VariadicString]]
    

    我不是一个打字专家,但我不认为你仅仅通过改变提示就可以做到这一点。Mypy无法知道在包含标记时调用了哪些重载方法,因为它可能属于任何一种用例

    例如,在这两种情况下,您的代码都允许将标记变量传递给
    *args
    变量,因此类型中没有任何提示告诉用户您不能尝试构造存储多个标记变量的部分实例

    我不知道这是否适用于您的解决方案,但由于您已经在手动检查第一个
    args
    变量的类型,您可以坚持使用标记或显式None调用该方法,将第二个重载更改为:

    @overload
        def __call__(self, tag: None, *args) -> 'Partial[Cls]':
            ...
    
    以及实施:

    # implementation
    def __call__(self, *args):
        if args and isinstance(args[0], Tag):
            return self.ctor(args[0], *self.args, *args[1:])
        # Don't add the None to the args.
        return Partial(self.ctor, *self.args, *args[1:])
    

    Partial
    有什么作用
    functools。Partial
    没有作用?@chepner
    Partial
    在非密封应用程序上自动应用,允许进一步的部分应用程序(它实际上是curries
    ctor
    )。密封更类似于
    partialmethod
    ,但在应用程序上显式绑定,而不是
    \uuuuu get\uuuu
    。在将实例原型绑定到DAG中之前,我们使用它进行增量准备。mypy知道哪种情况适用,并正确推断返回类型。它选择了第一个匹配的案例,也是最具体的案例。mypy的问题在于它坚持返回类型是兼容的。我不介意将
    *args
    args[0]
    指定为“非标记”“但是我不知道怎么做。对不起,我的意思是我认为mypy会抛出关于不兼容类型的错误,因为它无法知道用户在给出类型标记的args时永远不会期望第二种情况是调用的。我研究了将变量声明为非类型,但在文档中找不到任何内容。