why mypy-202301071445

Summary

  • What is mypy
  • Python Annotations
  • Annotation PEPs

Notes

What is Mypy?

Mypy 是一個 static type checker for Python3 & Python2.7.

嗯, static type checker. 一定會有一些朋友問說為什麼 python 需要 type checker. python 不是 dynamic-typed language 嘛? 要檢查 type 就不要寫 python 了阿

沒關係我們慢慢解釋

PEP — 483 The Theory of Type Hints && PEP 484 — Type Hints

PEP483, PEP484 是由 Guido van Rossum, Jukka Lehtosalo, Lukasz Langa 在 2014年提出的. 最後實現在了 python 3.5 上

# 就像這樣可以指定回傳的 type -> str
def func_a(a: str, b: str) -> str:
    return a + b

值得注意的一個特性是: python 的 type hints 是 Function Annotations, function annotations 是沒有強制力的. 如同字面上的意義它就是 function 的註解.

def foo(a: str) -> str:
    """Foo."""
        return 'hello' + a
 
# {function}.__annotations__ 可以呼叫出 Function Annotations
print(foo.__annotations__)
 
# {'a': <class 'str'>, 'return': <class 'str'>}
# 可以看到 `return` 內註明了我們回傳的屬性是 `str`

透過 Function Annotation, python 解放了一些可能性: 第三方套件可以拿到 function return type.

當然在 PEP484 中也有提到

It should also be emphasized that Python will remain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention.

對這樣很好, 創造了很多的可能性, 但我們希望保持簡單.

對這樣很好, python 永遠都是 dynamic-typed language

更多 PEP

PEP 526 — Syntax for Variable Annotations

Python Variable Annotations

init_var: int
 
my_var: int = 5
 
other_var: int
other_var = 5
 
print(__annotations__)
 
# {'init_var': <class 'int'>, 'my_var': <class 'int'>, 'other_var': <class 'int'>}

PEP 544 — Protocols: Structural subtyping (static duck typing)

Python Structural subtyping

簡單理解的話, 就是 golang 的 interface.

from typing import Iterator, Iterable
 
class Bucket:
    ...
    def __len__(self) -> int: ...
    def __iter__(self) -> Iterator[int]: ...
 
def collect(items: Iterable[int]) -> int: ...
result: int = collect(Bucket())  # Passes type check
 
# Bucker 因為實作了 Iterator 的 __len__ & __iter__ . 就可以當 Iterator 使用

PEP 586 — Literal Types

在 python3.8 新增的特性: Literal

from typing import Literal
 
def accepts_only_four(x: Literal[4]) -> None:
    pass
 
accepts_only_four(4)   # OK
accepts_only_four(19)  # Rejected

PEP 589 — TypedDict: Type Hints for Dictionaries with a Fixed Set of Keys

新增 TypedDict

from typing import TypedDict
 
class Movie(TypedDict):
    name: str
    year: int
 
movie: Movie = {
    'name': 'Blade Runner',
    'year': 1982,
}

PEP 591 — Adding a final qualifier to typing

新增了 @final && Final

@final 是 decorator

Final 是 annotation

from typing import final
 
@final
class Base:
    ...
 
class Derived(Base):  # Error: Cannot inherit from final class "Base"
    ...
from typing import final
 
class Base:
    @final
    def foo(self) -> None:
        ...
 
class Derived(Base):
    def foo(self) -> None:  # Error: Cannot override final attribute "foo"
                            # (previously declared in base class "Base")
        ...
# With an explicit type
ID: Final[float] = 1
 
# With no type annotation.
ID: Final = 1

所以為什麼需要 Mypy?

所以一切源於 PEP484, 後續的 PEP 是補充概念以及語法. 一般來說 Mypy 可以被運用在兩個地方

  1. Editor check
  2. Testing

我沒有辦法明確的跟你說 Mypy 是必要的且適合每個人. 比起寫一般的 python 會需要打更多字, 遵守更多規範. 但遵循這樣的規範寫程式會有幾個好處:

  • 易讀的程式碼
  • 易於重構的程式碼
  • 提升程式碼質量

Python type hints 是屬於 annotations. 它不是強制性的規範. 但大量使用可以有效提昇程式碼的 質量.

在日常開發以及正式的專案導入這樣的工具. 你的同事以及未來的你會更加感謝現在的你的.


References

PEP 3107 — Function Annotations