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

本文涉及的产品
Serverless 应用引擎 SAE,800核*时 1600GiB*时
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 【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/

目录
相关文章
|
5天前
|
存储 数据库 文件存储
Python中实现限定抽奖次数的机制的项目实践
本文介绍了如何在Python中实现限定抽奖次数的机制。通过选择合适的数据结构、设计清晰的逻辑流程以及编写简洁明了的代码,我们可以轻松地实现这一功能。同时,我们还探讨了如何对系统进行扩展和优化,以满足更多的实际需求。希望本文能对新手在开发抽奖系统时有所帮助。
|
16天前
|
监控 数据可视化 数据库
【python项目推荐】键盘监控--统计打字频率
【python项目推荐】键盘监控--统计打字频率
45 13
|
6天前
|
Python
Pycharm为Python项目配置环境不生效,解决办法
在PyCharm中,项目依赖配置更改后未生效。解决步骤包括:1) 查找`C:\Users\username\AppData\Roaming\JetBrains\PyCharm2022.2\options\jdk.table.xml`,2) 删除`&lt;jdk&gt;&lt;/jdk&gt;`标签内的旧配置内容,然后重启PyCharm以应用新目录。
14 0
Pycharm为Python项目配置环境不生效,解决办法
|
14天前
|
分布式计算 DataWorks 调度
阿里云DataWorks-部署自定义python项目问题
阿里云DataWorks-多种方式部署自定义python项目并进行调度
|
15天前
|
存储 Linux Docker
python项目 以docker形式打包部署全流程
在很久很久以前,我已经听过Docker的大名,当时服务着急上线虽然考虑过用Docker来部署我的服务,但是因为赶期的原因放弃了。 这两天因为华为云服务器到期,而且阿里云服务器优惠力度特别大的原因,我要把华为云服务器里的工程迁移到阿里云。 迁移的过程中,大量的时间精力浪费在了重装python,加载依赖,迁移项目,配置端口等环境配置的工作上。 我在想,如果当时用了Docker部署,我至于受这气?
|
11天前
|
数据采集 前端开发 JavaScript
Python爬虫技术:动态JavaScript加载音频的解析
Python爬虫技术:动态JavaScript加载音频的解析
|
12天前
|
Python
python项目依赖库打包发布方法
python项目依赖库打包发布方法
16 0
|
13天前
|
前端开发 数据库 Python
Python Django项目下的分页和筛选查询
在Django中实现分页功能,视图函数通过`Paginator`处理数据,每页显示10条记录。URL配置支持带参数和不带参数的分页请求。前端模板使用for循环展示分页数据,包括商品信息和状态按钮,并利用分页组件导航。筛选查询视图根据GET请求的`state`参数过滤上架或下架产品,同样实现分页功能。前端添加状态选择下拉框,分页链接携带查询参数`state`确保筛选状态在翻页时保持。
|
8天前
|
机器学习/深度学习 人工智能 前端开发
Python中的模块化编程
【6月更文挑战第17天】Python模块化编程与软件架构设计的关键在于拆分任务到独立模块,提高代码的可维护性、可重用性和可扩展性。例如,学生管理系统可分解为录入、查询和删除模块。MVC和MVVM架构模式有助于组织代码,而微服务和函数式编程将在未来发展中扮演重要角色。通过示例代码,读者能学习如何实现这些概念,提升项目开发效率和质量。
155 57
|
15天前
|
测试技术 虚拟化 云计算
GitHub高赞!速通Python编程基础手册,被玩出花了!
随着云时代的来临,Python 语言越来越被程序开发人员喜欢和使用,因为其不仅简单易学,而且还有丰富的第三方程序库和相应完善的管理工具。 从命令行脚本程序到 GUI程序,从图形技术到科学计算,从软件开发到自动化测试,从云计算到虚拟化,所有这些领域都有 Python 的身影。 今天给小伙伴们分享的这份手册采用以任务为导向的编写模式,全面地介绍了 Python 编程基础及其相关知识的应用,讲解了如何利用 Python 的知识解决部分实际问题。
GitHub高赞!速通Python编程基础手册,被玩出花了!