3.5:类型提示和方法重载

fan*_*ngo 6 python python-3.5

编辑:我目前误解了该功能。它不是为多次分派而设计的

注意:虽然可以使用此语法提供多分派实现,但它的实现需要使用 sys._getframe() ,这是不受欢迎的。此外,设计和实现高效的多分派机制很困难,这就是为什么以前的尝试被放弃而转而使用 functools.singledispatch() 的原因。(参见 PEP 443 ,尤其是它的“替代方法”一节。)将来我们可能会提出一个令人满意的多分派设计,但我们不希望这样的设计受到为 stub 中的类型提示定义的重载语法的约束文件。这两个特性也有可能彼此独立开发(因为类型检查器中的重载与运行时的多次分派相比具有不同的用例和要求——例如

====

我已经离开 Java 领域有一段时间了,现在我要回到 Python 3.5。我想使用新的类型提示功能,但我在方法重载方面遇到了麻烦。从我对该功能的阅读来看,应该支持这一点。

这是我正在学习的一个快速小类:

licensing.pyi(注意保华)

import typing
import gitlab


class LicensingChecker(object):
    @typing.overload
    def __init__(self, url: str, api_key: str) -> None: ...
    @typing.overload
    def __init__(self, gl: gitlab.Gitlab) -> None: ...

    def iter_projects(self) -> typing.Iterator[str]: ...
Run Code Online (Sandbox Code Playgroud)

许可.py

import gitlab
import string


class LicenseChecker(object):
    def __init__(self, gl):
        self.gl = gl

    def __init__(self, url, api_key):
        self.gl = gitlab.Gitlab(url, api_key)

    def iter_projects(self):
        p = set()
        for i in string.ascii_lowercase:
            for x in self.gl.projects.search(i):
                if x not in p:
                    p.add(x)
                    yield x.name
Run Code Online (Sandbox Code Playgroud)

这是一个玩具示例,但这个想法相当传统。我提供了两个构造函数,一个接受已经存在的 gitlab 客户端,另一个将实例化它。(这个脚本不需要双重构造函数,但我看到@typing.overload并想看看它是如何工作的。)

Pycharm 和 Cpython 似乎对这段代码很满意,但第一个构造函数无法访问——就像@typing.overload装饰器不起作用一样:

>>> import gitlab
>>> import licensing
>>> licensing.LicenseChecker(gitlab.Gitlab('https://blah.blah.blah/gitlab', 'hunter2'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() missing 1 required positional argument: 'api_key'
Run Code Online (Sandbox Code Playgroud)

有什么特别的我必须做才能让重载工作吗?目前,我只是调用内置的 REPL 或 ipython。

ken*_*ytm 5

无类型 Python 不支持重载。你的第二个__init__是覆盖第一个,因此是错误。您需要编写一个__init__带有运行时类型检查的单曲:

def __init__(self, arg1, arg2=None):
    if isinstance(arg1, gitlab.Gitlab) and arg2 is None:
        self.gl = arg1
    elif arg1 is not None and arg2 is not None:
        self.gl = gitlab.Gitlab(arg1, arg2)
    else:
        raise TypeError('........')
Run Code Online (Sandbox Code Playgroud)

(有一种functools.singledispatch可以只用一种参数改变类型来模拟重载,但它不适合你的情况)


@typing.overload装饰只是告诉类型检查可能存在的参数的多种组合,这并不意味着你可以写同名的两个不同功能的实现了。来自PEP 484

@overload如上所示使用装饰器适用于存根文件。在常规模块中,一系列@overload-decorated 定义必须紧跟一个@overload-decorated 定义(对于相同的函数/方法)。@overload-decorated定义仅用于类型检查的好处,因为它们将被覆盖在非@overload-decorated定义,而后者则是在运行时使用,但应该由一个类型检查被忽略。