【发布时间】:2019-02-27 13:43:36
【问题描述】:
我正在尝试对构造函数的部分应用进行类型提示,一旦提供了“标记”实例,该构造函数就会完全应用。这是通过一个包装类实现的,该类存储构造函数和任何部分应用的参数。由于包装器适用于多种类型,因此必须采用可变参数*args。
这会产生两种情况,它们所采用的参数不同:
- 申请:
(tag: Tag, ...) -> Cls - 商店:
(...) -> Partial[Cls]
值得注意的是,2. case 可能会或可能不会收到第一个参数。由于它们是可变参数,因此两者在它们的数量上重叠。这很容易实现。我尝试使用@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 或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]]
【问题讨论】:
-
Partial做了哪些functools.partial不做的事情? -
@chepner
Partial在非密封应用上自我应用,允许进一步的部分应用(它有效地咖喱ctor)。密封更类似于partialmethod,但在应用程序上显式绑定而不是__get__。我们使用它来增量准备实例原型,然后将它们绑定到 DAG 中。
标签: python pycharm type-hinting mypy