【问题标题】:Dynamic checking of type hints in Python 3.5+ [duplicate]Python 3.5+中类型提示的动态检查[重复]
【发布时间】:2020-11-30 05:52:24
【问题描述】:

typing 模块在 Python 3.5+ 中实现了类型提示。然而,这不是强制的,它目前似乎只是为了静态类型检查器的利益而存在,例如mypyPyCharm。我希望它是duck typing 的可行替代方案。

问题:有没有办法在 Python 3.7+ 中打开动态类型检查,而我在 Google 搜索中没有找到?例如,如果我定义

def greeting(name: str) -> str:
    return name

那么这应该在执行时失败:

greeting([12])

我不介意为此检查付出时间代价,因为出于我的目的,无论如何我都必须使用assert 语句手动实现它,并且类型提示更加简洁和描述性。

更新:下面的评论者指出typen 包将为我动态强制执行类型提示。所以这是一个肯定的答案,它将更新older question which was scoped to Python 3.6 and answered in the negative 的答案。我已经验证了规范的 typen 示例按预期工作:

from typen import enforce_type_hints

@enforce_type_hints
def halve_integer(a: int) -> float:
    return a / 2

halve_integer(5)  # 2.5
halve_integer(5.0)  # ParameterTypeError

唯一的缺点是每个功能都需要进行修饰才能获得行为,而不是只有一个开关可以为所有功能打开它。

更新 2:下面的回答还指出 pydantic 也可以解决问题。所以这是两个积极的解决方案。然而,pydantic 似乎更倾向于数据建模,并且对他们的验证装饰器有一些强烈的警告:

validate_arguments 装饰器处于测试版,它已被添加到 pydantic 在 v1.5 中是临时的。它可能会发生重大变化 在未来的版本中,它的接口在 v2 之前不会是具体的。 来自社区的反馈,而它仍然是临时的 非常有用;要么评论 #1205 要么创建一个新问题。

【问题讨论】:

  • 我很困惑,鸭子类型与静态类型不同。鸭子类型意味着如果它像鸭子一样嘎嘎叫,那么它就是鸭子。例如, list 和 str 都是可迭代的,因此在这方面它们可以被认为是相同的(如果您正在寻找可迭代的)[尽管这更像是 goose typing]。您也可以考虑使用装饰器或仅在您的代码中执行 if not isinstance(name, str): ....
  • 你可以复制这个包方法typen
  • Georgy,它的回答是否定的。尽管在解释器中启用它(通过添加与 mypy 中某处相同的代码)是一个如此明显的胜利,但我希望那里有人已经做到了。这就是问题的重点,所以看看某个地方是否有人。这个问题已被其他人提出这一事实意味着它不仅仅是我所需要的。切换到 OCaml 或 Axiom 对我来说是不可行的,我想让它在 Python 中工作。
  • 语法悔恨,我必须实现与类型提示相同的效果,但通过在函数参数的类上插入一堆断言语句来进行动态检查。我认为这是鸭子打字的一种形式。
  • 语法上的悔恨,是的,TypeN 似乎就是我要找的东西。我必须装饰每个函数以获取行为,这很尴尬,但如果它执行我想要的动态检查,我不介意这样做。您可以将其发布为答案吗?

标签: python python-3.x duck-typing python-typing


【解决方案1】:

我喜欢this线程中给出的答案,所以我会在这里给出:

您可以在 Python3 中使用注解,这可能有助于您获得静态类型的一些好处。

但是,如果要在 Python 中完全强制执行静态类型,那么它将不再是 Python。它是一种鸭式的动态语言,因此会失去所有的活力。如果你真的打算使用静态类型语言,最好不要使用 Python。

并引用 PEP 563 中的话:

Python 仍将是一种动态类型语言,作者不希望强制类型提示,即使按照惯例

就个人而言,有一些工具在运行时使用类型注释进行类型检查和验证,因为注释可以通过__annotations__ 属性访问。例如,pydantic,我在我的项目中使用。但它也有自己的特点,例如,它会尽可能地进行隐式类型转换。

一些例子:

from pydantic import validate_arguments, ValidationError, BaseModel
... 
... 
... @validate_arguments
... def sum_foo(a: int, b: int) -> int:
...     return a + b
... 
sum_foo("Hello", 1)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "pydantic/decorator.py", line 25, in pydantic.decorator.validate_arguments.wrapper_function
  File "pydantic/decorator.py", line 107, in pydantic.decorator.ValidatedFunction.call
  File "pydantic/main.py", line 346, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for SumFoo
a
  value is not a valid integer (type=type_error.integer)
class data(BaseModel):
...     a: int = 0
...     b: int = 1
...     
d = data(a=0, b="Hello")
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "pydantic/main.py", line 346, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for data
b
  value is not a valid integer (type=type_error.integer)

【讨论】:

  • 所以这是一种有罪的乐趣,但现在我们知道typenpydantic 都以肯定的方式回答了这个问题。
  • 或许在未来,它们会成为语言的一部分,但无论如何,你会有选择的余地
  • pydantic 目前似乎不如typen 自信。我在上述问题的更新中添加了他们的警告。我喜欢你的回答,但我更倾向于接受 typen 作为最终答案,如果建议它的评论者将发布作为答案。
猜你喜欢
  • 2021-12-25
  • 2019-01-18
  • 2016-03-17
  • 1970-01-01
  • 1970-01-01
  • 2018-08-07
  • 2021-12-08
  • 2015-12-10
相关资源
最近更新 更多