Python 靜態(tài)類型檢查 mypy 示例

以下所有例子都參考了最新版本的 Python 文檔與 mypy 文檔

必備條件

安裝最新版本的 Python 和 mypy

要學(xué)會(huì)按需配置自己的編輯器,比如我的 VSCode 就裝好了 Python 和 Pyright 擴(kuò)展

變量

age: int = 1

child: bool
if age < 18:
    child = True
else:
    child = False

常量

from typing import Final

RATE: Final = 3000

class Base:
    DEFAULT_ID: Final = 0

RATE = 300  # Error: can't assign to final attribute
Base.DEFAULT_ID = 1  # Error: can't override a final attribute

內(nèi)置類型

from typing import List, Set, Dict, Tuple, Optional

x: int = 1
x: float = 1.0
x: bool = True
x: str = "test"
x: bytes = b"test"

# 集合類型是首字母大寫的
# 元素的類型寫在中括號(hào)里面(泛型)
x: List[int] = [1]
x: Set[int] = {6, 7}

# 字典需要寫出 key 和 value 的類型
x: Dict[str, float] = {"field": 2.0}

# 元組需要寫出所有元素的類型
x: Tuple[int, str, float] = (3, "yes", 7.5)

# 用 Optional[] 表示可以為 None 的類型
x: Optional[str] = some_function()
# mypy 可以推斷出 if 語(yǔ)句里 x 不能為 None
if x is not None:
    print(x.upper())
# 如果 x 的值不可能為 None, 用 assert
assert x is not None
print(x.upper())

函數(shù)

from typing import Callable, Iterator, Union, Optional, List

def stringify(num: int) -> str:
    return str(num)

def plus(num1: int, num2: int) -> int:
    return num1 + num2

def f(num1: int, my_float: float = 3.5) -> float:
    return num1 + my_float

# callable (函數(shù)) 類型
x: Callable[[int, float], float] = f

# 生成器函數(shù)會(huì)返回可迭代的元素
def g(n: int) -> Iterator[int]:
    i = 0
    while i < n:
        yield i
        i += 1

from typing import ClassVar

class MyClass:
    attr: int
    # 實(shí)例變量可以有默認(rèn)值
    charge_percent: int = 100

    # 什么也不返回,就是返回 None

    def __init__(self) -> None:
        ...

    # 實(shí)例方法,省略 self 的類型
    def my_method(self, num: int, str1: str) -> str:
        return num * str1

# 類可以用作類型
x: MyClass = MyClass()

# 類變量
class Car:
    seats: ClassVar[int] = 4
    passengers: ClassVar[List[str]]

# 需要注意還沒(méi)定義就使用一個(gè)類會(huì)報(bào)錯(cuò)
def f(foo: A) -> int:  # Error
    ...

class A:
    ...

# 你可以使用字符串的形式來(lái)規(guī)避
def f(foo: "A") -> int:  # OK
    ...

Named tuples 命名元組

from typing import NamedTuple

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

p = Point(x=1, y="x")  # Error: Argument has incompatible type "str"; expected "int"

異步迭代器

from typing import AsyncIterator

async def gen() -> AsyncIterator[bytes]:
    lst = [b async for b in gen()]  # 推斷類型是 "List[bytes]"
    yield "no way"  # Error: Incompatible types (got "str", expected "bytes")

類型別名

AliasType = Union[List[Dict[Tuple[int, str], Set[int]]], Tuple[str, List[str]]]

def f() -> AliasType:
    ...

Dataclasses 數(shù)據(jù)類

from dataclasses import dataclass, field

@dataclass
class Application:
    name: str
    plugins: List[str] = field(default_factory=list)

test = Application("Testing...")  # OK
bad = Application("Testing...", "with plugin")  # Error: List[str] expected

泛型

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[int] 實(shí)例
stack = Stack[int]()
stack.push(2)
stack.pop()
stack.push("x")        # Type error

Literal types

PrimaryColors = Literal["red", "blue", "yellow"]
SecondaryColors = Literal["purple", "green", "orange"]
AllowedColors = Literal[PrimaryColors, SecondaryColors]

def paint(color: AllowedColors) -> None: ...

paint("red")        # OK
paint("turquoise")  # Error

Protocol 協(xié)議

實(shí)現(xiàn)結(jié)構(gòu)化子類型(靜態(tài)鴨子類型),可以當(dāng)成接口一樣用。

from typing import Iterable, Protocol

class SupportsClose(Protocol):
    def close(self) -> None:
       ...  # 函數(shù)體可以為空 (explicit '...')

class Resource:  # 沒(méi)有寫 SupportsClose
    def close(self) -> None:
       self.resource.release()

def close_all(items: Iterable[SupportsClose]) -> None:
    for item in items:
        item.close()

close_all([Resource(), open("some/file")])  # OK

Abstractmethod 抽象方法

方法可以有默認(rèn)的實(shí)現(xiàn),但是抽象方法規(guī)定必須在子類中實(shí)現(xiàn)。

from typing import Protocol
from abc import abstractmethod

class Example(Protocol):
    def first(self) -> int:
        return 42

    @abstractmethod
    def second(self) -> int:  # 沒(méi)有默認(rèn)實(shí)現(xiàn)
        raise NotImplementedError  # 防止這個(gè)方法被調(diào)用

Enum 枚舉

如果要像 C 語(yǔ)言一樣定義枚舉的話,也是用類來(lái)實(shí)現(xiàn)的。

from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

print(Color.RED)

這樣就能用類當(dāng)成枚舉一樣用了。

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容