Upgrade to Pro — share decks privately, control downloads, hide ads and more …

2021年の型駆動開発 / type-driven-design-in-2021-python

2021年の型駆動開発 / type-driven-design-in-2021-python

2021/05/07 CMScom techtalk

Peacock

May 07, 2021
Tweet

More Decks by Peacock

Other Decks in Programming

Transcript

  1. 2021
    年の型駆動設計(
    開発)
    CMScom Techtalk 2021/05/07

    View full-size slide

  2. 1. Type hint
    ざっくり復習
    i.
    関数定義のところから書くのがおすすめ
    2. Python3.10 style
    な type hinting
    i. 3.9, 3.10
    の Type hint
    系新機能を紹介
    ii. Pattern Matching, str.removeprefix()
    とか他の機能については話さない
    3.
    型を意識して設計するということ
    Table of Contents
    2 / 21

    View full-size slide

  3. 書いている → 少し⾶ばして後半に議論したい
    書いていない → 前に話したことを少し説明します
    はじめに: Type hint
    書いてますか?
    3 / 21

    View full-size slide

  4. 2020/09/11 TechTalk
    で話したこと

    View full-size slide

  5. 3.10 Style
    な type hinting

    View full-size slide

  6. 次ページから1
    つずつ⾒ていきます
    3.8
    → 3.9
    ⼩⽂字始まりの標準コレクション型ヒント
    3.9
    → 3.10
    Union
    型演算⼦ |
    引数仕様変数 (
    原題: Parameter Specification Variables)
    明⽰的な型エイリアス
    3.8
    から何が変わったか概要
    9 / 21

    View full-size slide

  7. 3.9
    からの新機能
    list, dict, Iterable, etc...
    PEP 585
    のページで列挙されている
    list[str]
    のように書く
    前に話した from typing import List
    は書かなくていい
    リリースから5
    年後(
    つまり2025
    年)
    には⾮推奨になる
    collections
    系の⾊々はライブラリ開発とかでもなければ普段使わなさそう
    re.Match, re.Pattern
    につくのは便利そう
    PEP 585:
    ⼩⽂字始まりの標準コレクション型ヒント
    10 / 21

    View full-size slide

  8. 3.10
    の新機能
    Union
    の糖⾐構⽂として |
    が使えるようになった
    str | int == Union[str, int]
    TypeScript
    とか Haskell
    がこの記法を使っている
    より直感的で記述量が減るのでうれしい
    def square(number: int | float) -> int | float:
    return number ** 2
    # isinstance() でも使える
    >>> isinstance(1, int | str)
    True
    PEP 604: Union
    型演算⼦ |
    11 / 21

    View full-size slide

  9. 3.10
    の新機能
    Callable[T, R]
    の T
    に今までは tuple, Generics
    を渡せなかった
    str, int
    などの型 or elipsis ( ... )
    なら⼤丈夫だった
    以下の例のように書けるようになる
    デコレーターを書くときに便利そう(
    まだ使ったことない)
    P = ParamSpec("P")
    def validation: (f: Callable[P, str]) -> Callable[P, bool]:
    def inner(s: str) -> bool: retrun s.isascii()
    PEP 612:
    引数仕様変数 (
    原題: Parameter Specification
    Variables)
    12 / 21

    View full-size slide

  10. 3.10
    の新機能
    エイリアス型がエイリアスであるとより明⽰的になる
    # Before 3.10
    JsonLikeDict = dict[Union[str, int], Any]
    # After 3.10
    JsonLikeDict: TypeAlias = dict[Union[str, int], Any]
    # Union を新しい記法にしたバージョン
    JsonLikeDict: TypeAlias = dict[str | int, Any]
    PEP 613:
    明⽰的な型エイリアス
    13 / 21

    View full-size slide

  11. これらを Python3.7, 3.8
    のコードで使うには from __future__ import
    annotations
    を書く
    __future__ :
    今後実装予定のモジュール
    3.6
    には⾮対応: PEP 563
    dunder module (?)
    なので⼀番最初に書く
    from __future__ import annotations
    from datetime import datetime
    from __future__ import annotations
    を書く
    14 / 21

    View full-size slide

  12. 型を意識して設計する
    「型駆動開発」を1
    年くらい実践して得た Best practices

    View full-size slide

  13. Optional
    は便利だけどコードが肥⼤化していく原因
    def get_content() -> str | None:
    r = request.get("https://example.com")
    if r.status_code != 200: # ここがガード(早期リターン)
    logging.warning("HTTP response is %d!", r.status_code)
    return None
    return r.text
    ↑の関数を使うときにまたガードを書いて None
    を返すかもしれない
    結果、先のメソッドまでガードを書く必要があり可読性が落ちる
    Optional
    をなるべく使わない
    16 / 21

    View full-size slide

  14. この場合なら raise RuntimeError
    なりしてしまったほうがすっきりする
    Python
    は例外を発⽣させるコストが(
    ⽐較的)
    低いのでパフォーマンスも
    ⼤丈夫なはず
    Python
    に null
    安全なメソッドがないのも⼀因だけど、あるとそれはそれで
    乱⽤してしまう
    Null
    安全とは: Null(None)
    を渡しても例外が発⽣しないメソッド
    Plone
    の View
    とかがこれで書けるかはわからないけど、内部に使っている
    関数レベルでならできそう
    17 / 21

    View full-size slide

  15. Language Server(LS):
    エディタで補完機能などをサポートしてくれる機能
    Pylance(VS Code extension), Jedi
    が有名
    裏でプロセスが⾛っていて、エディタと通信して動く
    ⾃明な型は書かない、Language Server
    の推論がやってくれる
    変数に型ヒントつける基準は推論が効かなくなる( Any
    判定される)
    とき
    必要があればガードをする。そうすれば取りうる型の範囲が狭まる
    次ページにサンプルがあります
    Language Server
    の⼒を借りる
    18 / 21

    View full-size slide

  16. i: int = random.randint(1, 5) # `: int` は書かない
    r = request.get("http://example.com/api.json")
    d: dict = json.loads(r.text) if r.status_code == 200 else {}
    if d == {}: # エラー処理
    logger.error("Response is empty!")
    raise RuntimeError
    pass # ここで `d` は空dictの可能性がなくなっている
    19 / 21

    View full-size slide

  17. 関数定義のところから Type hint
    書いていきましょう
    Python3.10 style
    な type hinting
    ⼩⽂字始まりの標準コレクション型ヒント (3.9)
    Union
    型演算⼦ | (3.10)
    引数仕様変数 (3.10)
    明⽰的な型エイリアス (3.10)
    型を意識して設計すると全体の⾒通しが良くなるのでおすすめ
    まとめ
    20 / 21

    View full-size slide

  18. Thank you for listening!
    This slide is made by marp

    View full-size slide