python - 类型提示条件变量应用
问题描述
我正在尝试对构造函数的部分应用进行类型提示,一旦提供了“标记”实例,该构造函数就会完全应用。这是通过一个包装类实现的,该类存储构造函数和任何部分应用的参数。由于包装器适用于多种类型,因此它必须采用 variadic *args
。
这会产生两种情况,它们的不同之处在于它们采用的论点:
- 申请:
(tag: Tag, ...) -> Cls
- 店铺:
(...) -> Partial[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 目前需要一个显式的方法调用,但这不是我的问题)。使用显式非标记扩展第二个重载(tag: Any, ...) -> Partial[Cls]
并不能解决问题。这两个工具要么报告类型不匹配、不兼容的重载,要么回退到Any
or 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 无法知道在包含 Tag 时调用了哪些重载方法,因为它可能属于任一用例。
例如,就目前而言,您的代码将允许*args
在任何一种情况下将 Tag 变量传递给变量,因此类型提示中没有任何内容告诉用户您不能尝试构造存储多个 Tag 变量的部分实例.
我不知道这是否适用于您的解决方案,但由于您已经在手动检查第一个args
变量的类型,您可以坚持使用标记或显式无调用该方法,更改第二个重载到:
@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:])
推荐阅读
- php - 可以在 Laravel / PHPUnit 中模拟模型方法输出吗?
- php - PDO 查询未将数据插入 MySQL 数据库,未显示任何错误
- oauth-2.0 - 使用 oAuth 退出 Microsoft AAD 时,有没有办法避免“选择帐户”提示?
- php - 如何更改 wordpress 主题的语言?
- python - 如何使用没有 IOError 的 GCS 路径加载文件?
- csv - 当它不是文件的第一行时如何自动设置标题?
- ssms - SQL Server Management Studio 17.9 字体大小不起作用
- javascript - $(document).ready 故障,$(document).resize 有效 - 相同的处理程序
- java - OpenJDK 11.0.2 的 Spring Boot 2.0 请求映射问题
- python - 选择多个单词的首字母