能够强制执行类型提示吗?

19
在Python中使用“类型提示”符号是否有优势?
import sys
def parse(arg_line: int) -> str:
    print (arg_line) # passing a string, returning None

if __name__ == '__main__':
    parse(' '.join(sys.argv[1:]))

对我来说,它似乎复杂化了语法,而没有提供任何实际的好处(除了可能在开发环境中)。基于此:
  • 是否有计划在Python语言本身中包含type约束条件?
  • 拥有“类型提示”的优点是什么?我不能将其轻松地放入docstring或其他东西中吗?

根据我所知,在Python代码库中也没有看到这种情况-大多数类型都是手动强制执行的,例如:argparse.py以及我在https://github.com/python/cpython/blob/3.7/Lib/中浏览过的任何其他文件。


4
Python不是一种静态类型语言。如果你想使用静态类型语言,你需要使用除Python之外的其他语言。 - Pranav Hosangadi
2
“Python 中是否有计划在语言本身中包含类型约束?”没有。目标是标准化人们注释函数签名的方式。主要目的是为了方便创建静态或运行时类型检查等内容。但从未计划也没有意愿在语言本身中强制执行。您可以阅读相关的 PEP 484PEP 3107 - juanpa.arrivillaga
请注意,mypy 是 Python 的事实上“官方”静态类型检查器。事实上,Guido 目前正在开发它。 - juanpa.arrivillaga
在发布问题之前,我们希望您进行适当的研究。请更新问题,说明您从查找类型提示用法方面所学到的内容。 - Prune
1
Python花了四分之一个世纪才开始使用类型提示... - D.L
3个回答

13
有没有计划让Python语言本身包含类型约束?
几乎可以肯定没有,而且在下一个主要版本(4.x)之前绝对不会有。
拥有“类型提示”的优势是什么?我难道不能把它放在文档字符串或其他地方吗?
我能想到的一些优势如下:
- 类型提示可以通过工具(如mypy)进行验证。 - IDE和其他工具可以使用类型提示来提供提示和建议。例如,当你调用一个函数并刚刚写下foo(时,IDE可以根据类型提示显示一个附近的框,显示foo(x: int, y: List[int])。作为开发者,你的优势在于你可以获得你需要的准确信息,而不必整理整个文档字符串。 - 类型提示可以被像functools.singledispatch这样的模块或像multipledispatch这样的外部库使用,以添加额外的与类型相关的功能(在这种情况下,根据名称和类型而不仅仅是名称来调度函数调用)。

1
请注意,当这篇文章写于2020年9月时,“下一个重要版本”可能是3.9(于2020年10月5日发布)或者更合理地是3.10(于2021年10月4日发布)。 但我们还没有达到那个阶段。 - undefined

7

利用类型提示的一种选择是使用 type_enforced 模块。关于官方Python支持,似乎仍然不太可能在不久的将来直接强制执行类型提示。

进入 type_enforced,该包允许您利用类型提示。它支持输入和输出类型。只有指定的类型会被强制执行。也支持多个可能的输入,因此您可以指定诸如 int 或 float 之类的内容。

输入类型首先在函数调用时进行惰性验证,如果有效,则处理函数,然后验证返回值。

有一些限制,例如不支持嵌套类型结构。例如,不能将类型指定为整数列表,而只能是列表。您需要在函数内部验证列表中的项目。

pip install type_enforced

>>> import type_enforced
>>> @type_enforced.Enforcer
... def my_fn(a: int , b: [int, str] =2, c: int =3) -> None:
...     pass
...
>>> my_fn(a=1, b=2, c=3)
>>> my_fn(a=1, b='2', c=3)
>>> my_fn(a='a', b=2, c=3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/conmak/development/personal/type_enforced/type_enforced/enforcer.py", line 47, in __call__
    return self.__validate_types__(*args, **kwargs)
  File "/home/conmak/development/personal/type_enforced/type_enforced/enforcer.py", line 83, in __validate_types__
    self.__check_type__(assigned_vars.get(key), value, key)
  File "/home/conmak/development/personal/type_enforced/type_enforced/enforcer.py", line 56, in __check_type__
    self.__exception__(
  File "/home/conmak/development/personal/type_enforced/type_enforced/enforcer.py", line 37, in __exception__
    raise TypeError(f"({self.__fn__.__qualname__}): {message}")
TypeError: (my_fn): Type mismatch for typed function (my_fn) with `a`. Expected one of the following `[<class 'int'>]` but got `<class 'str'>` instead.

1
这个软件包真是一个巨大的生产力助推器!非常感谢你!:) - undefined

1
简短回答是,根据其他答案所述,没有计划提供标准支持。这里没有提到的一个替代方案是pydanticpydantic中的BaseModel本质上是一个Python数据类,在其构造函数中执行类型验证(还具有方便的方法来验证/解组json)。对于您的特定问题,有一个非常值得注意的特性......如果您传递一个可以解析为正确类型的字符串(例如'1' -> int | '1.5' -> float),只有在值无法强制转换为正确类型时,pydantic才会抛出错误,并为您执行转换(可以使用strict types或设置strict=True并使用BaseModel.model_validate来规避此问题)。
它不像type_enforced那样只使用您的注释/提示(通过将函数包装在检查它们的另一个函数中),您仍然需要使用BaseModel的构造函数/验证器来强制执行类型,例如:
from pydantic import BaseModel,ValidationError
from typing import Union
from json import dumps as to_json

class Foo(BaseModel):
    a:int
    b: Union[int,str] = 2
    c: int = 3

def my_fn(foo: Foo) -> None:
    pass

good: Foo = Foo(a=1,b=2,c=3)

also_good: Foo = Foo(a='1',b=2,c='3')

good_dict: dict = {"a":1,"b":2,"c":3}
bad_dict_strict: dict = {"a":'1',"b":2,"c":'3'}

good_dict_json: str = to_json(good_dict)

from_good_dict: Foo = Foo(**good_dict)
from_good_dict_json: Foo = Foo.model_validate_json(good_dict_json)

try:
    bad: Foo = Foo(a='b',b='a',c=1)
except ValidationError as ve:
    print(ve)
try:
    bad_strict: Foo = Foo.model_validate(bad_dict_strict,strict=True)
except ValidationError as ve:
    print(ve)

my_fn(good)
my_fn(also_good)
my_fn(from_good_dict)
my_fn(from_good_dict_json)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接