将python项目从动态迁移到静态

本文涉及的产品
函数计算FC,每月15万CU 3个月
云原生网关 MSE Higress,422元/月
可观测链路 OpenTelemetry 版,每月50GB免费额度
简介: 【6月更文挑战第29天】本文介绍**mypy 是 Python 的静态类型检查器,它结合动态和静态类型的优势,提供编译时类型检查而无运行时开销。 文中在类的示例中,展示了如何为方法添加类型注解,以增强类的安全性。泛型允许创建可复用的类型安全容器,如 Stack 类,它可以用

简介

本文介绍项目迁移工具mypy, 使用 mypy,开发者可以享受静态类型的好处,如减少调试时间、增强代码可读性和易维护性。示例展示了如何在不同场景下应用 mypy,包括对字典、类和泛型的类型检查。

在处理字典时,添加类型注解能明确变量类型,提高代码清晰度。在类的示例中,展示了如何为方法添加类型注解,以增强类的安全性。泛型允许创建可复用的类型安全容器,如 Stack 类,它可以用于不同类型的元素。

mypy 支持 Python 的泛型,允许用户定义泛型类,如 Stack[T],并能够捕获类型错误。在 Python 3.8 之前,内置类型不直接支持泛型实例化,需要使用 typing 模块。

mandala曼德罗符号.png

1 mypy 类型检查

Mypy 是 Python 的可选静态类型检查器,旨在结合动态(或“鸭子”)类型和静态类型的优点。
Mypy 将 Python 的表达能力和便利性与强大的类型系统和编译时类型检查相结合。
Mypy 类型检查标准 Python 程序;使用任何 Python VM 运行它们,基本上没有运行时开销。

将现有代码迁移到静态类型,一次一个函数。您可以在程序、模块或表达式中自由混合静态类型和动态类型。无需放弃动态类型——在有意义的时候使用静态类型。通常只需添加函数签名即可为您提供静态类型的代码。Mypy 可以推断其他变量的类型。

2 类型检测

编译时类型检查有不少优点,尤其在大型项目中。 mypy支持对python项目做如下操作:

  • 静态类型使得通过更少的调试更容易找到错误。
  • 通常更容易维护。
  • 类型声明充当机器检查文档。静态类型使您的代码更易于理解和修改,而不会引入错误。
  • 将程序从动态类型迁移到静态 类型
  • 您可以使用动态类型开发程序并在代码成熟后添加静态类型,或者将现有的 Python 代码迁移到静态类型。

    简单例子:一个hello word 程序,展示mypy如何使用的

          mypy main.py --python-version 3.6
    
          mypy main.py
    
          (venvbackend) # mypy resource.py  --python-version 3.5
                  resource.py:15: error: Variable annotation syntax is only supported in Python 3.6 and greater
                  Found 1 error in 1 file (errors prevented further checking)
                  (venvbackend) root@fk:/data/code/hello_world# mypy resource.py  --python-version 3.6
                  resource.py:30: note: Revealed type is "builtins.int"
                  (venvbackend) root@fk:/data/code/hello_world# cat resource.py
                  from typing import Dict, Optional
    

代码如下:

    ## python3.x

    def square(x:float, y:float) -> float:
        return x * y

    # typing module was added in pyton3.5

    def log(s:str, *, filename:Optional[str] = None) -> None:
        ...

    # add in python3.6
    x: int = 5

    from typing import NamedTuple

    class NT(NamedTuple):
        x: int
        y: int

    ## added in python3.9

    # old way
    def print_items(dct:Dict[str, str]) -> None:...

    # def print_items[dct:dict[str,str]) -> None: ...

    reveal_type(NT(1,2).y) # mypy 检测提示不匹配的地方 resource.py:30: note: Revealed type is "builtins.int"

    import dataclasses

    @dataclasses.dataclass
    class Tiem(object):
        a: str
        b: int
    reveal_type(Tiem("n",12).a)
  • 统计字典中词频

    在此示例中,我们为变量 d 添加了一个显式类型声明,因为它在局部上下文中并不明显。

    Mypy检查动态类型字典

    Display the frequencies of words in a file.

    import sys
    import re

    if not sys.argv[1:]:

       raise RuntimeError('Usage: wordfreq FILE')
    

    d = {}

    with open(sys.argv[1]) as f:

       for s in f:
           for word in re.sub('\W', ' ', s).split():
               d[word] = d.get(word, 0) + 1
    

    Use list comprehension

    l = [(freq, word) for word, freq in d.items()]

    for freq, word in sorted(l):

       print('%-6d %s' % (freq, word))
    

Mypy检查静态类型字典
如下程序,显示一个文件中的词频

    import sys
    import re
    from typing import Dict

    if not sys.argv[1:]:
        raise RuntimeError('Usage: wordfreq FILE')

    d = {}  # type: Dict[str, int]

    with open(sys.argv[1]) as f:
        for s in f:
            for word in re.sub('\W', ' ', s).split():
                d[word] = d.get(word, 0) + 1

    l = [(freq, word) for word, freq in d.items()]

    for freq, word in sorted(l):
        print('%-6d %s' % (freq, word))
  • 简单类型

    在本例中,我们选择使用整数来表示余额。例如,这在游戏中会很好,但在其他应用程序中,不同的类型会更有意义。

    Mypy检查带有动态类型的类型

    class BankAccount:
         def __init__(self, initial_balance=0):
             self.balance = initial_balance
         def deposit(self, amount):
             self.balance += amount
         def withdraw(self, amount):
             self.balance -= amount
         def overdrawn(self):
             return self.balance < 0
    
     my_account = BankAccount(15)
     my_account.withdraw(5)
     print(my_account.balance)
    

    Mypy检查带有静态类型的类

    class BankAccount:
         def __init__(self, initial_balance: int = 0) -> None:
             self.balance = initial_balance
         def deposit(self, amount: int) -> None:
             self.balance += amount
         def withdraw(self, amount: int) -> None:
             self.balance -= amount
         def overdrawn(self) -> bool:
             return self.balance < 0
    
     my_account = BankAccount(15)
     my_account.withdraw(5)
     print(my_account.balance)
    
  • 带生成器的素数筛

    Mypy 检查带有动态类型,一个从2到无穷大的素数筛生产器。 可以永远生成素数

    import itertools
    def iter_primes():

        numbers = itertools.count(2)
    
        while True:
            # Get the first number from the iterator
            # (always a prime)
            prime = next(numbers)
            yield prime
    
            # This code iteratively builds up a chain
            # of filters...
            numbers = filter(prime.__rmod__, numbers)
    

    for p in iter_primes():

       if p > 1000:
           break
       print(p)
    

静态类型的 Mypy检查支持
静态类型的一个从2到无穷大的素数筛生产器。

    import itertools
    from typing import Iterator

    def iter_primes() -> Iterator[int]: 
         numbers = itertools.count(2)

         while True:

             prime = next(numbers)
             yield prime

             numbers = filter(prime.__rmod__, numbers)

    for p in iter_primes():
        if p > 1000:
            break
        print(p)

3 检查泛型:在python中 使用 mypy

泛型

先介绍如何定义自己的泛型类,这些泛型类采用一个或多个类型参数,类似于内置类型,例如list[X]. 用户定义的泛型是一个中等高级的特性,你可以在不使用它们的情况下走得更远——请随意跳过本节,稍后再回来。

定义泛型类

内置集合类是泛型类。泛型有一个或多个类型参数,可以是任意类型。例如,具有类型参数and ,并且具有类型参数。

dict[int, str]intstrlist[int]int

程序还可以定义新的通用类。这是一个非常简单的泛型类,代表一个堆栈:

以下堆栈类创建一个类型为 T 的空列表

   from typing import TypeVar, Generic

    T = TypeVar('T')

    class Stack(Generic[T]):
        def __init__(self) -> None:

            self.items: list[T] = []

        def push(self, item: T) -> None:
            self.items.append(item)

        def pop(self) -> T:
            return self.items.pop()

        def empty(self) -> bool:
            return not self.items

该类Stack可用于表示任何类型的堆栈: Stack[int]、等。Stack[tuple[int, str]]

使用Stack类似于内置容器类型:

创建一个空堆栈实例,最报错 类型错误

    stack = Stack[int]()
    stack.push(2)
    stack.pop()
    stack.push('x')        # 类型错误

类型推断也适用于用户定义的泛型类型: Stack[int] 的 参数类型推断

        def process(stack: Stack[int]) -> None: ...

        process(Stack())   
  • 内部通用类

    您可能想知道在运行Stack时索引时会发生什么 。
    Stack索引返回一个通用别名 , 它在实例化Stack时返回原始类的实例:

     print(Stack)
         __main__.Stack
    
     print(Stack[int])
    
         __main__.Stack[int]
    
     print(Stack[int]().__class__)
    
         __main__.Stack
    

    泛型别名可以被实例化或子类化,类似于真实的类,但上面的例子说明了类型变量在运行时被擦除。

    泛型Stack实例只是普通的 Python 对象,除了重载索引运算符的元类之外,它们没有额外的运行时开销或由于泛型而产生的魔力。

    需要注意,在 Python 3.8 及更低版本中,内置类型 list和dict其他类型不支持索引。
    这就是在模块中使用别名List等 的原因 。

    索引这些别名会为您提供一个通用别名,类似于通过在更新的 Python 版本中直接索引目标类而构造的通用别名:Dicttyping

    3.8版本及以下版本如下:

     from typing import List
     List[int]
     typing.List[int]
    

    请注意,中的通用别名typing不支持构造实例:

     from typing import List
    
     List[int]()
     Traceback (most recent call last):
    
     ...
     TypeError: Type List cannot be instantiated; use list() instead
    

    用户定义的泛型类typing 可以用作另一个类的基类,包括泛型和非泛型。例如:

     from typing import Generic, TypeVar, Mapping, Iterator
    
     KT = TypeVar('KT')
     VT = TypeVar('VT')
    
     class MyMap(Mapping[KT, VT]):  # This is a generic subclass of Mapping
         def __getitem__(self, k: KT) -> VT:
             ...  # Implementations omitted
         def __iter__(self) -> Iterator[KT]:
             ...
         def __len__(self) -> int:
             ...
    
     items: MyMap[str, int]  # Okay
    
     class StrDict(dict[str, str]):  # This is a non-generic subclass of dict
         def __str__(self) -> str:
             return f'StrDict({super().__str__()})'
    
     data: StrDict[int, int]  # Error! StrDict is not generic
     data2: StrDict  # OK
    
     class Receiver(Generic[T]):
         def accept(self, value: T) -> None:
             ...
    
     class AdvancedReceiver(Receiver[T]):
         ...
    

    如果您希望 mypy 将用户定义的类视为映射(以及 Sequence序列等),则必须添加显式基类。

    这是因为 mypy 不对 这些 ABC 使用结构子类型Iterable,这与使用结构子类型的更简单的协议不同。

    Generic如果有其他包含类型变量的基类,则可以从基类中省略,例如 在上面的示例中。

    如果你包含在基础中,那么它应该列出其他基础中存在的所有类型变量(或者更多,如果需要的话)。
    类型变量的顺序由以下规则定义:Mapping[KT, VT]Generic[...]

    如果Generic[...]存在,则变量的顺序总是由它们在Generic[...] 中的顺序决定。

    如果没有,则所有类型变量都按字典顺序收集(即按第一次出现)。

类型别名不定义新类型。对于泛型类型别名,这意味着用于别名定义的类型变量的变化不适用于别名。
参数化的通用别名被简单地视为原始类型,并替换了相应的类型变量。

4 小结

总之,mypy 提升了 Python 代码的健壮性,尤其是对于大型项目,它提供了类似强类型语言的额外保障,同时保持了 Python 的灵活性。 各语言发展最后都越来越相似。

参考文档

https://mypy.readthedocs.io/en/stable/

目录
相关文章
|
24天前
|
机器学习/深度学习 数据采集 数据可视化
Python 数据分析:从零开始构建你的数据科学项目
【10月更文挑战第9天】Python 数据分析:从零开始构建你的数据科学项目
48 2
|
5天前
|
弹性计算 Linux iOS开发
Python 虚拟环境全解:轻松管理项目依赖
本文详细介绍了 Python 虚拟环境的概念、创建和使用方法,包括 `virtualenv` 和 `venv` 的使用,以及最佳实践和注意事项。通过虚拟环境,你可以轻松管理不同项目的依赖关系,避免版本冲突,提升开发效率。
|
18天前
|
JSON 搜索推荐 API
Python的web框架有哪些?小项目比较推荐哪个?
【10月更文挑战第15天】Python的web框架有哪些?小项目比较推荐哪个?
38 1
|
24天前
|
机器学习/深度学习 人工智能 算法
【玉米病害识别】Python+卷积神经网络算法+人工智能+深度学习+计算机课设项目+TensorFlow+模型训练
玉米病害识别系统,本系统使用Python作为主要开发语言,通过收集了8种常见的玉米叶部病害图片数据集('矮花叶病', '健康', '灰斑病一般', '灰斑病严重', '锈病一般', '锈病严重', '叶斑病一般', '叶斑病严重'),然后基于TensorFlow搭建卷积神经网络算法模型,通过对数据集进行多轮迭代训练,最后得到一个识别精度较高的模型文件。再使用Django搭建Web网页操作平台,实现用户上传一张玉米病害图片识别其名称。
49 0
【玉米病害识别】Python+卷积神经网络算法+人工智能+深度学习+计算机课设项目+TensorFlow+模型训练
|
2月前
|
机器学习/深度学习 人工智能 算法
【新闻文本分类识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台
文本分类识别系统。本系统使用Python作为主要开发语言,首先收集了10种中文文本数据集("体育类", "财经类", "房产类", "家居类", "教育类", "科技类", "时尚类", "时政类", "游戏类", "娱乐类"),然后基于TensorFlow搭建CNN卷积神经网络算法模型。通过对数据集进行多轮迭代训练,最后得到一个识别精度较高的模型,并保存为本地的h5格式。然后使用Django开发Web网页端操作界面,实现用户上传一段文本识别其所属的类别。
87 1
【新闻文本分类识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台
|
1月前
|
存储 开发工具 Python
【Python项目】外星人入侵项目笔记
【Python项目】外星人入侵项目笔记
36 3
|
1月前
|
前端开发 JavaScript API
惊呆了!学会AJAX与Fetch API,你的Python Web项目瞬间高大上!
在Web开发领域,AJAX与Fetch API是提升交互体验的关键技术。AJAX(Asynchronous JavaScript and XML)作为异步通信的先驱,通过XMLHttpRequest对象实现了局部页面更新,提升了应用流畅度。Fetch API则以更现代、简洁的方式处理HTTP请求,基于Promises提供了丰富的功能。当与Python Web框架(如Django、Flask)结合时,这两者能显著增强应用的响应速度和用户体验,使项目更加高效、高大上。
47 2
|
22天前
|
机器学习/深度学习 算法框架/工具 Python
基于深度学习的手写数字识别项目GUI(Deep Learning Project – Handwritten Digit Recognition using Python)
基于深度学习的手写数字识别项目GUI(Deep Learning Project – Handwritten Digit Recognition using Python)
43 0
|
2月前
|
前端开发 Python
前后端分离的进化:Python Web项目中的WebSocket实时通信解决方案
在现代Web开发领域,前后端分离已成为一种主流架构模式,它促进了开发效率、提升了应用的可维护性和可扩展性。随着实时数据交互需求的日益增长,WebSocket作为一种在单个长连接上进行全双工通讯的协议,成为了实现前后端实时通信的理想选择。在Python Web项目中,结合Flask框架与Flask-SocketIO库,我们可以轻松实现WebSocket的实时通信功能。
55 2
|
2月前
|
机器学习/深度学习 人工智能 算法
【果蔬识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台
【果蔬识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台。果蔬识别系统,本系统使用Python作为主要开发语言,通过收集了12种常见的水果和蔬菜('土豆', '圣女果', '大白菜', '大葱', '梨', '胡萝卜', '芒果', '苹果', '西红柿', '韭菜', '香蕉', '黄瓜'),然后基于TensorFlow库搭建CNN卷积神经网络算法模型,然后对数据集进行训练,最后得到一个识别精度较高的算法模型,然后将其保存为h5格式的本地文件方便后期调用。再使用Django框架搭建Web网页平台操作界面,实现用户上传一张果蔬图片识别其名称。
51 0
【果蔬识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台