如何在python 3.6中使用类型提示?

sz81bmfz  于 2022-12-01  发布在  Python
关注(0)|答案(4)|浏览(149)

我注意到Python 3.5和Python 3.6增加了很多关于静态类型检查的特性,所以我尝试了下面的代码(在Python 3.6中,稳定版本)。

from typing import List

a: List[str] = []
a.append('a')
a.append(1)
print(a)

让我惊讶的是,Python没有给我一个错误或警告,尽管1被附加到了一个只应该包含字符串的listPycharm检测到了类型错误,并给了我一个关于它的警告,但它并不明显,也没有在输出控制台中显示出来,我担心有时我可能会错过它。我想要以下效果:
1.如果很明显我使用了错误的类型,就像上面显示的那样,抛出一个警告或错误。
1.如果编译器不能可靠地检查我使用的类型是对还是错,请忽略它。
可能吗?也许mypy可以做到这一点,但我更喜欢使用Python 3.6风格的类型检查(如a: List[str]),而不是mypy中使用的注解风格(如# type List[str])。我很好奇,在原生Python 3.6中是否有一个开关来实现我上面提到的两点。

9udxz4iz

9udxz4iz1#

Python运行时完全忽略了类型提示,只有mypy和Pycharm的集成检查器这样的第三方工具才能检查类型提示。还有很多不太知名的第三方工具在编译时或运行时使用类型注解进行类型检查,但大多数人使用mypy或Pycharm的集成检查器AFAIK。
实际上,我怀疑在可预见的未来,类型检查是否会被集成到Python中--参见PEP 484(引入了类型注解)和PEP 526(引入了变量注解)的“非目标”部分,以及Guido在这里的评论。
我个人很高兴类型检查与Python更紧密地集成在一起,但是Python社区似乎还没有准备好或者不愿意做这样的改变。
mypy的最新版本应该理解Python 3.6变量注解语法和注解样式语法。事实上,变量注解基本上是Guido最初的想法(Guido目前是mypy团队的一员)--基本上,mypy和Python中对类型注解的支持几乎是同时开发的。

dz6r00yl

dz6r00yl2#

这可能吗?也许mypy可以做到,但是我更喜欢使用Python 3.6风格的类型检查(比如a: List[str]),而不是mypy中使用的注解风格(比如# type: List[str])。我很好奇原生Python 3.6中是否有一个开关来实现我上面提到的两点。
Python不可能为您做这些;你 * 可以 * 使用mypy来进行类型检查(PyCharms内置的检查器也应该这样做)。除此之外,mypy也 * 不限制 * 你只能输入注解# type List[str],你可以像在Python 3.6中那样使用变量注解,所以a: List[str]同样工作得很好。
对于mypy,因为这个版本是最新的,您需要安装typed_ast,并按照mypy文档中的说明使用--fast-parser--python-version 3.6执行mypy

**更新:**现在不需要--fast-parser--python-version 3.6

完成这个操作后,mypy会很好地检测到第二个操作在a: List[str]上的不兼容性。假设您的文件名为tp_check.py,其中包含语句:

from typing import List

a: List[str] = []
a.append('a')
a.append(1)
print(a)

使用上述参数运行mypy(必须先运行pip install -U typed_ast):

python -m mypy --fast-parser --python-version 3.6 tp_check.py

捕获错误:

tp_check.py:5: error: Argument 1 to "append" of "list" has incompatible type "int"; expected "str"

正如 * 在许多其他关于Python类型提示的回答中 * 所指出的,mypyPyCharm的类型检查器是执行验证的,* 而不是Python本身 *。Python目前并不使用这些信息,它只将其存储为元数据,并在执行过程中忽略它。

uajslkp6

uajslkp63#

Python中的类型注解并不意味着是强制类型的,任何涉及运行时静态类型依赖的东西都意味着如此根本的改变,以至于继续把结果语言称为“Python”都没有意义。
注意,Python的动态特性允许使用纯Python代码构建一个外部工具来执行运行时类型检查。这会使程序运行(非常)缓慢,但可能适合某些测试类别。
当然,Python语言的基本原理之一是,任何东西都是对象,你可以在运行时对对象执行任何操作,如果对象没有与所尝试的操作一致的接口,它将在运行时失败。
本质上是静态类型的语言以不同的方式工作:在编译阶段,编译器会为所有合适的对象创建空格和槽,并且在不一致的类型化时中断编译。
Python的类型检查允许任意数量的工具来完成这一任务:在实际运行应用程序之前的某个步骤中断并发出警告(但独立于编译本身)。但语言的本质不能改变为实际要求对象在运行时遵守--在编译步骤本身验证类型和中断将是人为的。
尽管如此,我们可以预期Python的未来版本可能会在Python运行时本身上合并编译时类型检查--最有可能是通过可选的命令行开关。(我认为它永远不会是默认的--至少不会破坏构建--也许它可以成为发出警告的默认设置)
因此,Python在运行时不需要静态类型检查,因为这样它就不再是Python了。但至少有一种语言同时使用了动态对象和静态类型--Cython语言。它实际上是作为Python超集工作的。人们应该期望Cython很快就能将新的类型提示语法合并为实际的类型声明。(当前,它对可选的静态类型变量使用不同的语法)

toe95027

toe950274#

pydantic包有一个validate_arguments装饰器,它在运行时检查类型提示。你可以将这个装饰器添加到所有你想要强制类型提示的函数或方法中。
我编写了一些代码来帮助自动化整个模块,这样我就可以为我的测试套件启用运行时检查,以帮助调试,但随后对使用该库的代码关闭运行时检查,这样就不会对性能产生影响。

import sys
import inspect
import types
from pydantic import validate_arguments

class ConfigAllowArbitraryTypes:
    """allows for custom classes to be used in type hints"""
    
    arbitrary_types_allowed = True

def add_runtime_type_checks(module):
    """adds runtime typing checks to the given module/class"""

    if isinstance(module, str):
        module = sys.modules[module]
    
    for attr in module.__dict__:
        obj = getattr(module, attr)
        
        if isinstance(obj, types.FunctionType):
            setattr(module, attr, validate_arguments(obj, config=ConfigAllowArbitraryTypes))
        elif inspect.isclass(obj):
            # recursively add decorator to class methods
            add_runtime_type_checks(obj)

在我的测试套件中,我通过使用模块的名称调用add_runtime_type_checks来添加装饰器。

import mymodule

def setup_module(module):
    """executes once"""
    
    add_runtime_type_checks('mymodule')

def test_behavior():
   ...

请注意,使用pydantic时,在类型检查时可能会进行一些意外的转换,因此如果所需的类型是int,并且您传递了函数0.2,则它会将其静默地强制转换为0,而不是失败。原则上,您可以使用typen库的enforce_type_hints装饰器进行几乎相同的操作。但它不执行递归检查(因此不能使用list[int]之类的类型,只能使用list)。

相关问题