python 属性 setter 的参数的类型注释应该是什么?

What should the type annotation for a python property setter's argument be?

python 是否对 PEP-484 类型注释对 属性 setter 的论点有立场?我看到两个选项,这两个选项似乎都有效(根据我和 mypy 的说法)。

考虑:

from dataclasses import dataclass
from typing import Any

@dataclass
class Foo:
    _bar: int = 1

    @property
    def bar(self) -> int:
        return self._bar

    @bar.setter
    def bar(self, value) -> None:
        self._bar = value

问题是:

Should @bar.setter's value argument be typed with typing.Any or with int?

一方面,在 setter 中,具有预期类型 hint 将有助于执行验证,但另一方面,传入值可能是任何类型。

不过有一点值得注意; mypy 确实警告对 属性 setter:

的错误分配
f = Foo()
f.bar = 2     # Ok
f.bar = "baz" # Incompatible types in assignment (expression has type "str", variable has type "int") 

我相信这是因为 Foo.bar 的显示类型是 int,而不是来自 @bar.settervalue 参数的类型。

我在 python/cpython and python/typeshed 个项目中搜索了示例,但没有得出任何明确的结论。

我对现代 python 非常有经验,并且很乐意阅读 cpython 来源(在 C 或 python 本身)。引用 PEP 或包含来自 cpython 或 mypy 维护者的输入的答案将是理想的。

这个问题非常适合基于强烈的意见,但是,我认为对于用预期类型(即 def bar(self, value: int) -> None:)注释的突变器可能会有更有力的论据。首先,注释的实现是为了帮助静态分析,而不是提供任何真正的运行时好处(据我所知,目前还不知道。来自 PEP 484 rationale:

Of these goals, static analysis is the most important. This includes support for off-line type checkers such as mypy, as well as providing a standard notation that can be used by IDEs for code completion and refactoring.

如果类型注释主要是为了在静态分析、linting 等中受益,那么您希望能够检查您是否传递了错误的类型,而不是在运行时可能发现您有例如,使用 isinstance 没有通过类型检查正确处理参数。

这也意味着我们可以事半功倍,因为更具体的 int 注释将消除我们添加这些类型保护的需要:

def bigger_fun(n: Any) -> None:
    if isinstance(n, float):
        # do something...
    else
        # dosomething else...

def smaller_fun(n: int) -> None:
    # do something

您将确切地知道您将收到什么类型以及如何处理它,而不是需要实现不同的多个条件分支来首先将参数转换为预期值,然后再对其进行操作。这将允许您仅使用最少的内部逻辑/处理就可以使您的增变器尽可能苗条。

如果您传递错误的类型,您的 IDE 或静态分析工具至少会在为 smaller_fun 传递 float 时警告您。另一方面,使用 Any 可能会对某些类型产生意外行为,从而引入难以追踪的运行时错误。

现在更具体地针对您的问题,同一 PEP 涉及 The Meaning of Annotations

@property 注释的使用

Type checkers are expected to attempt to infer as much information as necessary. The minimum requirement is to handle the builtin decorators @property, @staticmethod and @classmethod.

这意味着您可以预期 @property 注释应该会像您期望的那样正常运行。没有任何特殊处理。

虽然 python 本质上是一种动态类型的语言,但像 mutator 这样的方法与特定值(以及因此类型)紧密相关,并且应该只做一件事而不是做许多事情中的一件。因此,虽然对于像 __gt__ 这样的比较方法,它可能会对不同的类型执行不同的操作,但它可能会采用 Any 值,因此 mutator 应该采用尽可能窄的范围。

最后,尽管类型提示不是而且可能永远不应该是强制性的,但所有最流行的 python IDE 都自动支持类型提示,例如 Pycharm。他们通常会发出警告,即使另一个程序员可能没有注释类型,但可以安全地推断类型。这意味着即使在使用带有类型提示的库时,带有 int 注释的修改器仍然比 Any 注释对最终用户提供更多信息和有用。