Successfully reported this slideshow.

Python と型ヒント (Type Hints)

31

Share

Python と型ヒント
2015-10-10 Tetsuya Morimoto

YouTube videos are no longer supported on SlideShare

View original on YouTube

自己紹介
●
森本 哲也
– twitter: @t2y
●
白ヤギコーポレーション所属
– カメリオAPI開発 ( 興味がある方はブースへ )
●
エキスパート Python プログラミング共訳
●
プログラミング言語歴
– Python (...
Upcoming SlideShare
Pythonと型チェッカー
Pythonと型チェッカー
Loading in …3
×
1 of 40
1 of 40

More Related Content

Related Books

Free with a 14 day trial from Scribd

See all

Python と型ヒント (Type Hints)

  1. 1. Python と型ヒント 2015-10-10 Tetsuya Morimoto
  2. 2. 自己紹介 ● 森本 哲也 – twitter: @t2y ● 白ヤギコーポレーション所属 – カメリオAPI開発 ( 興味がある方はブースへ ) ● エキスパート Python プログラミング共訳 ● プログラミング言語歴 – Python (3 年 ) – → Java (3 年 ) – → Go ( 半年 )
  3. 3. 求人 ● 自然言語処理や機械学習の技術を駆使して製品 開発をしたい人募集!
  4. 4. Pythonのパラドックス ● 2004 年 8 月 Paul Graham – Python を学ぼうとするプログラマーは賢い ● Python 2.3.4 (2004-05-27): 2.3 の時代 – 新しい言語を学ぶ人たちはプログラミングが好き であり、仕事に役立つから学ぶわけではない – 曲解 : 「優秀な Java プログラマーを採用したかっ たら Python プログラマーを雇えば良い」 ● いまは成り立たない
  5. 5. Python3 のパラドックス ● 2015 年 10 月 – Python3 を現場に導入しようとするプログラマーは 賢い – Python3 を導入する人たちはプログラミングが好き であり、現場で役立つから導入するわけではない – 曲解 : 「優秀な Python プログラマーを採用した かったら Python3 を現場に導入すれば良い」 ● それっぽい? ( 自己責任で! )
  6. 6. 概要 ● 背景 – 漸進的型付け→関数アノテーション→ mypy ● PEP 484 〜型ヒント〜 – 目的 – 型ヒントのために定義された型 (Any, Union, etc) – ジェネリクス ● 懸念 – 型ヒントの批判、 Typed Clojure の現在 ● 展望 – 構造的部分型 (Structual Subtyping) へ Python3.5
  7. 7. 背景
  8. 8. 背景 : 漸進的型付け (Gradual Typing) ● Jeremy Siek 氏と Walid Taha 氏が 2006 年に 考案した型システム – 静的型付けと動的型付けを組み合わせられる – 双方の型システムの良いとこ取りしたもの – Object 型ではなく Any 型を導入しなければならな い背景の説明 ● PEP 484 の理論的背景 ● 入門記事 : 漸進的型付けとは何か
  9. 9. 背景 : 関数アノテーション (PEP3017) ● Python3.0 で導入 (PEP3017) – 2008-12-03 リリース – 関数に任意のアノテーションを書く仕組み ● PEP 484 を支える基本的な仕組み – 関数アノテーションの用途を型アノテーションに 限定した – 関数オブジェクトの __annotations__ 属性に保持 されるのは同じ
  10. 10. 背景 :mypy プロジェクト (1) ● http://mypy-lang.org/ ● 作者 : Jukka Lehtosalo 氏 – プログラマーを 6 年、マネージャーを 2 年 – 2009 年からケンブリッジ大学の博士課程 ● 2012 PyCon Finland – MYPY: A PYTHON VARIANT WITH SEAMLESS DYNA MIC AND STATIC TYPING ● 静的型付けと動的型付けの融合 – PyPy や Cython がライバル、 GIL のない VM
  11. 11. 背景 :mypy プロジェクト (2) ● 現時点の mypy ができること – GIL のない VM – 型アノテーション→ PEP 484 で標準化 – 静的型チェッカーとしての Lint ツール ● PEP 484 では型チェッカーは提供しない
  12. 12. PEP 484 型ヒント
  13. 13. 型ヒント : サンプルコード # -*- coding: utf-8 -*- import json import sys from pprint import pformat from typing import Any, Dict def pretty_format(data: Dict[str, Any]) -> str: return pformat(data, width=1) def main() -> None: raw_data = sys.argv[1] # type: str data = pretty_format(json.loads(raw_data)) print(data) data.append('mypy') main() $ mypy tutorial.py tutorial.py: note: In function "main": tutorial.py:14: error: "str" has no attribute "append"
  14. 14. 型ヒント : 目的 ● 型アノテーションの構文を標準化 ● 静的解析 – Lint ツール ( オフライン型チェッカー ) ● IDE – コード補完やリファクタリング機能に利用 – Type Hinting in PyCharm ● ドキュメンテーション – 型アノテーションがそのまま使える? Python は 動的型付け言語
  15. 15. 型ヒント :Sphinx 俺がやる!
  16. 16. 型ヒント :Any 型 ● 全ての型は Any 型のサブタイプ – object 型と似ているが、型チェッカーによる扱い が異なる ● object 型 – 型チェッカーは値のほとんどの操作を拒否する ● Any 型 – 型チェッカーは値の全ての操作を許容する
  17. 17. any_and_object.py: note: In function "object_func": any_and_object.py:8: error: Unsupported left operand type for + ("object") 型ヒント :Any 型と object 型 from typing import Any def any_func(x: Any, y: Any) -> Any: return x + y def object_func(x: object, y: object) -> object: return x + y mypy object 型の操作は型チェカーでエラーとなる 理論的背景は「漸進的型付けとは何か」を参照
  18. 18. 型ヒント : 直和型 (Union Type) ● 直和型 – 関数型プログラミング言語でよく使われるデータ型 – 典型的にはツリーのデータ構造を簡潔に表現できる ● 単一の引数に対して少数の期待される型の集合 をとる e = Union[Employee, Sequence[Employee]] # OCaml バリアント型 type day = Sun | Mon | Tue | Wed | Thu | Fri | Sat # 第7回「代数データ型」でいろいろなデータを表してみる
  19. 19. 型ヒント :Optional 型 ● 直和型の応用 ● Union[T1, None] と Optional[T1] は等価 – None を取り得ることが明示される – None は多くの操作が型エラー ● Java の NullPointerException はこりごり – @Nonnull, @Nullable のアノテーションを明示 – Collections.emptyList(), Collections.emptyMap() ● Null 安全を型システムで保証する世界へ
  20. 20. 型ヒント :Optional 型の例 from typing import Optional, Union def func1(data: Optional[int]) -> int: return 3 + data def func2(data: Union[int, None]) -> int: return 3 + data def func3(data: int=None) -> int: return 3 + data これらは等価な型ヒント ・デフォルト引数に None を指定したときも Optional 型として扱う TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
  21. 21. 型ヒント : ジェネリクス ● コンテナー内の要素に期待する型を示す ● TypeVar というファクトリーを使って型変数を 定義する from typing import Sequence, TypeVar T = TypeVar('T') # Declare type variable def first(l: Sequence[T]) -> T: # Generic function return l[0]
  22. 22. 型ヒント :TypeVar ● TypeVar のコンストラクタに渡す型変数を表す 文字列と代入する変数名は同じでなければいけ ない from typing import TypeVar X = TypeVar('X') # correct Z = TypeVar('Y') # error mypy generics_typevar.py:4: error: Unexpected TypeVar() argument value
  23. 23. 型ヒント :TypeVar の例 ● typing モジュールにいくつか定義されてる # Some unconstrained type variables. These are used by the container types. T = TypeVar('T') # Any type. KT = TypeVar('KT') # Key type. VT = TypeVar('VT') # Value type. T_co = TypeVar('T_co', covariant=True) # Any type covariant containers. V_co = TypeVar('V_co', covariant=True) # Any type covariant containers. VT_co = TypeVar('VT_co', covariant=True) # Value type covariant containers. T_contra = TypeVar('T_contra', contravariant=True) # Ditto contravariant. # A useful type variable with constraints. This represents string types. # TODO: What about bytearray, memoryview? AnyStr = TypeVar('AnyStr', bytes, str) 型の別名
  24. 24. 型ヒント : 型別名と直和型 ● 型別名と直和型の違い – 型パラメーターとなり得るか from typing import Generic, TypeVar, Union AnyStr = TypeVar('AnyStr', str, bytes) class AnyStrNode(Generic[AnyStr]): ... UnionStr = Union[str, bytes] class UnionStrNode(Generic[UnionStr]): ... mypy error: Free type variable expected in Generic[...]
  25. 25. 型ヒント : ユーザー定義ジェネリック型 ● Generic という抽象基底クラスを継承する from typing import Generic, TypeVar T = TypeVar('T') class Stack(Generic[T]): def __init__(self) -> None: self.items = [] # type: List[T] def push(self, item: T) -> None: self.items.append(item) def pop(self) -> T: return self.items.pop() s = Stack() # type: Stack[int] s.push(3) s.push(5) s.pop() s.push('x') s = Stack[int]() ... mypy mypy generics_stack.py:19: error: Argument 1 to "push" of "Stack" has incompatible type "str"; expected "int" error: Generic type not valid as an expression any more (use '# type:' comment instead)
  26. 26. 型ヒント : 型消去 ● ユーザー定義のジェネリック型はインスタンス 化できる from typing import TypeVar, Generic T = TypeVar('T') class Node(Generic[T]): ... x = Node[T]() # The type inferred for x is Node[T]. print('x.__class__:', x.__class__) y = Node[int]() # The type inferred for y is Node[int]. print('y.__class__:', y.__class__) z = Node() # Inferred type is Node[Any]. print('z.__class__:', z.__class__) 実行時に型情報は 保持していない $ python generics_type_erasure.py x.__class__: __main__.Node[~T] y.__class__: __main__.Node[~T] z.__class__: __main__.Node[~T]
  27. 27. 型ヒント : 数値型階層 (numeric tower) ● numbers モジュールに抽象基底クラスを定義 complex 型 int/float 型を許容する def add_int(num: int) -> int: return num + 1 print(add_int(1)) print(add_int(2.4)) print(add_int(complex('3+4J'))) # floatは割愛 def add_complex(num: complex) -> complex: return num + complex('1+2J') print(add_complex(7)) print(add_complex(8.14)) print(add_complex(complex('9+10J'))) mypy error: Argument 1 to "add_int" has incompatible type "float"; expected "int" error: Argument 1 to "add_int" has incompatible type "complex"; expected "int"
  28. 28. 型ヒント : スタブファイル ● 型チェッカーのための拡張モジュール ● 型ヒントを Python モジュールではなく外部の ファイルに書き出すためのもの ● 拡張子は .pyi ● 主には ( 型ヒントがない ) サードパーティのラ イブラリと一緒に型ヒントを使うため – スタブを集める typeshedリポジトリもある ● TypeScript でいうところの DefinitelyTyped
  29. 29. 型ヒント : スタブの例 class tzinfo: def tzname(self, dt: Optional[datetime]) -> str: ... def utcoffset(self, dt: Optional[datetime]) -> int: ... def dst(self, dt: Optional[datetime]) -> int: ... def fromutc(self, dt: datetime) -> datetime: .. class timezone(tzinfo): utc = ... # type: tzinfo min = ... # type: tzinfo max = ... # type: tzinfo def __init__(self, offset: timedelta, name: str = '') -> None: ... def __hash__(self) -> int: ... datetime.pyi
  30. 30. 型ヒント : その他 ● 呼び出し可能オブジェクト ● 抽象ジェネリック型 ● 上界をもつ型変数 ● 共変性と反変性 ● 前方参照 ● キャスト ● ... PEP 484 を読んでください ...
  31. 31. 懸念
  32. 32. 懸念 :(Python に ) 必要か? ● Python に型ヒントは本当に必要なのか? ● Revenge of the Types: 型の復讐 (翻訳) – Armin Ronacher 氏の問題提起 – 欲しいのは強力な型システムそのもの ≠型ヒント ● 代数的データ型 (Algebraic data type) の議論 – Python は言語の意味論が壊れてる? ● Python 側実装と C 言語側実装による意味論の違い ● CPython インタープリターの最適化のため
  33. 33. 懸念 :Typed Clojure の現状 ● Why we’re no longer using Core.typed ● CircleCI が本番環境で 2 年間使っていたが、止 めてしまった話 – 型チェックが遅くてインタラクティブに開発でき ない – core.typed が Clojure 言語全てをカバーしてない – サードパーティライブラリの型アノテーションの メンテが大変
  34. 34. 展望
  35. 35. 展望 : 構造的部分型 (Structual Subtyping) ● 構造的部分型の型チェックの前提案 – プロトコルという概念を導入 ● Sized, Iterable, Iterator など – クラスの継承関係ではなく、メソッド実装の有無 で型チェックを行う (Go 言語のポリモルフィズム ) – うまくいけば 3.6 の PEP に出てくるかも? ● クラスの継承関係で部分型を定義するのを公称 的部分型 (Nominal Subtyping) と言う
  36. 36. 展望 : 構造的部分型への期待? ● [Python-ideas] Structural type checking for PEP 484 – Cory Benfield (hyper の作者 ) からの返信 – 自分は型ヒントに特に関心のない開発者の 1 人 だったんだけど、これは型チェックに関して懸念 していたことに対応できるものだと思う
  37. 37. まとめ
  38. 38. 型ヒントの所感 ● 型ヒントが成功を収めるかどうかは懐疑的 – 型ヒントが Python3 のキラーアプリというのは まだ楽観的過ぎると私は思う→プロパガンダ ● みんな強力な型システムを求めている – NullPointerException の発生しない世界 ● 型システムとプログラミングの関係を学ぶ – プログラミングの幅が広がり楽しくなる!
  39. 39. Happy Coding With Type Hints

×