+ 作るログイン
図を読み込んでいます…
1 / 23
JS から来た人の Python ― どこから来て、なぜ動くのか / 全19章

Python 15:型ヒントと道具

発行
アニメモ公式
版
第1版
改訂
コマ
全23

型ヒントは実行時に何もしないことを、int と書いた関数に文字列を渡して実際に確かめます。型ヒントは __annotations__ に残るだけで、検査するのは mypy や pyright という別の道具です。None の扱い、型ヒントの無い関数、ruff の check と format、pytest がテストを見つける決まりと assert の読み解き方を、TypeScript・ESLint・Prettier・Jest と並べて見ます。最後に、pytest・mypy・エディタが「どの python の上で動いているか」で結果が変わる理由と、確かめ方をまとめます。

-> None と書いたけど、実行するとき何かしてる?

第14章で tenki の cli.py に、こう書きました。

python
def main() -> None:
    ...

-> None は「この関数は何も返さない」という意味の型ヒント(type hint。引数や戻り値の型を書き添えたもの)です。def to_fahrenheit(celsius: float) -> float: のように、引数の後ろに : 型、閉じかっこの後ろに -> 型 と書きます。

TypeScript を書いたことがあれば、これは見慣れた形でしょう。そして、TypeScript ではこう期待します。型が合わない値を渡したら、コンパイルの段階でエラーになって止まる。

では Python ではどうでしょうか。第1章で見たとおり、Python には「先にコンパイルして実行ファイルを作る」段階がありません。python main.py と打てば、その場で読んで実行します。

疑問この章の答えの場所
型ヒントは、実行時に何をする?何もしない(実際に動かして確かめる)
では、型を検査するのは誰?mypy・pyright という別の道具
ruff・pytest は何をする道具?書き間違いの検出と整形・テスト
道具やエディタは、どの python を見ている?章の後半で、実際に結果が変わる様子を見る

最後の疑問は、第12章の venv、第14章の .pth とつながる、この章の山場です。「pytest を打ったら ModuleNotFoundError」「エディタで import に赤い波線」の原因は、ほとんどこれです。

※2026年9月時点(Python 3.13)の情報です。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 1 / 23
  • -> None と書いたけど、実行するとき何かしてる?
  • # src/tenki/cli.py(第14章)
  • TypeScript の感覚
  • def main() -> None:
  • コード
  • ...
  • 型が合わなければ、コンパイルで止まる
  • def to_fahrenheit(celsius: float) -> float:
  • return celsius * 9 / 5 + 32
  • CPU
  • Python では?
  • コンパイルの段階が無い。では誰が見る?
  • この章の疑問:型ヒントは実行時に何をする? 誰が検査する? ruff・pytest は何をする?
  • そして、それらの道具やエディタは「どの python」を見ているのか?
  • ひらめき
  • TypeScript なら型が合わないと止めてくれる。Python の型ヒントは、どうなのか。

型ヒントは「注意書きの札」。守らせるのは別の検査員

型ヒントを一言でいうと、関数に貼った注意書きの札です。「ここには float を入れてください」「戻ってくるのは float です」と書いてあります。

ところが、実際に関数を動かす python は、この札を読み飛ばします。札に何と書いてあっても、渡されたものをそのまま使って計算します。

札を読んで「使い方が札と違う」と文句を言うのは、mypy(マイパイ)などの型チェッカー(型の検査員)です。型チェッカーは、コードを実行せずに読むだけで、札と実際の使い方の食い違いを探します。

いつ誰が型ヒントを見るか何のために
書いているときエディタ(VS Code の Pylance など)補完・赤い波線・説明の表示
実行する前mypy・pyright食い違いを一覧にする
実行するときpython は見ない―
実行するとき(例外)dataclass・FastAPI など自分で札を読んで、仕組みに使う

つまり Python の型は、「書く」と「検査する」が別の人の仕事になっています。書くのはあなた、検査するのは別の道具で、しかもその道具を呼ぶかどうかもあなた次第です。この「別の道具」という点が、後半の「どの python で動いているか」の話につながります。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 2 / 23
  • 型ヒントは「注意書きの札」。守らせるのは別の検査員
  • CPU
  • 書類
  • 検索
  • python(実行)
  • 読み飛ばす
  • 型ヒント(札)
  • 読む
  • mypy(検査員)
  • 札は見ない
  • celsius: float
  • 札と使い方を照らす
  • いつ
  • 誰が型ヒントを見るか
  • 書いているとき
  • エディタ(VS Code の Pylance など)が赤い波線を出す
  • 実行する前
  • mypy・pyright が検査して、エラーを一覧にする
  • 実行するとき
  • python は見ない(FastAPI などが自分で読むことはある)
  • ひらめき
  • python は札を読み飛ばして実行する。札を読んで文句を言うのは mypy などの道具だけ。

本当に何もしない:int と書いても str が通る

本当に python が札を無視するのか、確かめます。

python
def double(n: int) -> int:
    return n * 2

print(double(21))
print(double("21"))   # str を渡した
bash
$ python double.py
42
2121

エラーは出ませんでした。n: int と書いてあるのに、"21" という文字列がそのまま入り、"21" * 2 が計算されました。Python では文字列に整数を掛けると「その回数つなげる」という意味になるので、"2121" が返ってきます。-> int と書いた戻り値が str なのに、それも確かめていません。

書いてある札実際に渡したものpython の反応
n: int"21"(str)何も言わない
-> int"2121"(str)を返した何も言わない

これが「型ヒントは実行時に何もしない」の意味です。止まらないどころか、間違った答えを返して先へ進むことがあります。tenki なら、気温が "2121" のような文字列で表示されてしまうかもしれません。

JS でも、function double(n) { return n * 2 } に "21" を渡すと 42 になります(JS は文字列を数に変換して掛ける)。どちらも「型が違っても動いてしまう」言語ですが、変換のしかたは違います。TypeScript が型の検査を足すのと同じように、Python では型チェッカーを足します。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 3 / 23
  • 本当に何もしない:int と書いても str が通る
  • def double(n: int) -> int:
  • $ python double.py
  • return n * 2
  • 42
  • 2121
  • print(double(21))
  • print(double("21")) # str を渡した
  • 注意
  • エラーは出ない。"21" * 2 は
  • 「文字列を2回つなぐ」なので
  • "2121" が返って、そのまま進む
  • -> int と書いてあっても、戻り値が str かどうかも確かめない
  • 型ヒントは「こう使ってね」という作者の説明で、python にとってはただの注釈
  • ひらめき
  • 型ヒントに合わない値を渡しても、python は止めない。結果がおかしくなるだけ。

止まるとしたら、ずっと後の「計算できない行」で

もちろん、型が合わないせいで止まることもあります。ただし、止まる場所に注意してください。

python
def to_fahrenheit(celsius: float) -> float:
    return celsius * 9 / 5 + 32

print(to_fahrenheit(20))
print(to_fahrenheit("20"))
68.0
Traceback (most recent call last):
  File "hint.py", line 5, in <module>
    print(to_fahrenheit("20"))
  File "hint.py", line 2, in to_fahrenheit
    return celsius * 9 / 5 + 32
TypeError: unsupported operand type(s) for /: 'str' and 'int'

エラーの最後の2行を見ると、止まったのは関数の中の2行目です。呼び出した5行目で「float じゃないよ」と止まったのではありません。

順に追うと、"20" * 9 は文字列の繰り返しなので "202020202020202020" になり、ここまでは進みます。次の / 5 で「文字列を数で割れない」となって、ようやく TypeError が出ました。

場所何が起きたか
5行目(呼んだ所)何も確かめずに、"20" を渡す
2行目の * 9文字列の繰り返しとして成功してしまう
2行目の / 5ここで初めて失敗

本物のコードでは、間違った値が何段も関数を渡り歩いた先で、ようやく止まります。原因(間違った値を渡した所)と、止まった所が離れるので、探すのが大変です。型チェッカーは、この「原因の所」を実行前に指してくれます。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 4 / 23
  • 止まるとしたら、ずっと後の「計算できない行」で
  • def to_fahrenheit(celsius: float) -> float:
  • 68.0
  • return celsius * 9 / 5 + 32
  • Traceback (most recent call last):
  • File "hint.py", line 5, in <module>
  • print(to_fahrenheit(20))
  • print(to_fahrenheit("20"))
  • print(to_fahrenheit("20"))
  • File "hint.py", line 2, in to_fahrenheit
  • return celsius * 9 / 5 + 32
  • TypeError: unsupported operand type(s)
  • for /: 'str' and 'int'
  • "20" * 9 は "202020…"(文字列の繰り返し)まで進み、次の / 5 で初めて失敗する
  • 呼んだ瞬間ではなく、計算できなくなった行で止まる。原因から遠い場所で落ちるので探しにくい
  • ひらめき
  • エラーは呼んだ行ではなく、関数の中の計算で出る。型ヒントのおかげで止まったのではない。

型ヒントは捨てられず、関数に「メモ」として残る

python は型ヒントを読み飛ばす、と書きましたが、捨ててはいません。関数の __annotations__ という属性に、辞書として残っています。

python
>>> def label(city: str, temp: int) -> str:
...     return f"{city}: {temp}℃"
>>> label(24, "Tokyo")        # 逆に渡しても動く
'24: Tokyo℃'
>>> label.__annotations__
{'city': <class 'str'>, 'temp': <class 'int'>, 'return': <class 'str'>}
>>> x: int = "hello"
>>> type(x)
<class 'str'>

x: int = "hello" も通りました。変数の型ヒントも、ただのメモです。

このメモは、読みたい人が読めるようになっています。代表的なのが、第7章で出てきた dataclass と、第17章で使う FastAPI です。

読む人いつ何に使うか
python 本体―使わない
mypy・pyright実行前食い違いを探す
エディタ書いているとき補完・波線・説明
dataclass実行時型ヒントのある変数を「欄」として集め、__init__ を作る
FastAPI実行時届いた値が型どおりか確かめ、違えば 422 エラーを返す

つまり「型ヒントは実行時に何もしない」は、正確には「python 本体は型ヒントを使わないが、ライブラリが自分で読んで使うことはある」です。FastAPI が入力を検証してくれるのは、Python の機能ではなく、FastAPI が __annotations__ を読んで自分で確かめているからです。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 5 / 23
  • 型ヒントは捨てられず、関数に「メモ」として残る
  • >>> def label(city: str, temp: int) -> str:
  • 読む人
  • いつ・何のために
  • ... return f"{city}: {temp}℃"
  • >>> label(24, "Tokyo") # 逆に渡しても動く
  • python 本体
  • 読まない
  • '24: Tokyo℃'
  • >>> label.__annotations__
  • mypy・pyright
  • 実行前に矛盾を探す
  • {'city': <class 'str'>, 'temp': <class 'int'>,
  • 'return': <class 'str'>}
  • エディタ
  • 補完・波線・説明
  • >>> x: int = "hello"
  • >>> type(x)
  • dataclass
  • 実行時に欄の一覧を作る
  • <class 'str'>
  • FastAPI
  • 実行時に入力を検証(第17章)
  • ひらめき
  • __annotations__ に辞書で入っている。python は見ないが、読みたい人(ライブラリ)は読める。

TypeScript との違い:「コンパイルで止まる」段階が無い

TypeScript と Python の流れを並べると、図のようになります。

TypeScript では、tsc(TypeScript のコンパイラ)が .ts を読み、型を検査してから、型を取り除いた .js を作ります。node が動かすのは、型の無くなった .js です。型の検査は「JS にする途中の段階」に組み込まれています(ただし tsc も、既定では型エラーがあっても .js を書き出します。止めるかどうかは設定と、CI でエラーを失敗扱いにするかどうかで決まります)。

Python には、この「途中の段階」がありません。python main.py は、型ヒントを読み飛ばしてそのまま実行します。型の検査は、mypy を自分で呼んで初めて行われます。

TypeScriptPython
型を書くn: numbern: int
実行するまでの段階tsc で JS にする無い(そのまま実行)
型の検査tsc がその段階で行うmypy・pyright を別に呼ぶ
実行時の型消えている__annotations__ に残っている
検査しないで動かすできる(下の注)いつもそう

注目したいのは、最近の Node.js の動きです。Node.js 22.18 と 23.6 以降は、.ts ファイルを直接 node app.ts で動かせます。このとき Node.js は型の部分を消して実行するだけで、型の検査はしません。型の検査は別に tsc --noEmit で行う、という形です。

これは、Python が最初からやっている形とほぼ同じです。「実行は型を見ない。検査は別の道具」。TypeScript から来た人は、tsc の「検査」の部分だけを mypy が受け持つと考えると、すっきりします。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 6 / 23
  • TypeScript との違い:「コンパイルで止まる」段階が無い
  • 書類
  • app.ts
  • tsc
  • 検索
  • 型を検査
  • 合わなければ報告
  • 型を消す
  • 書類
  • app.js
  • CPU
  • node
  • 書類
  • main.py
  • そのまま実行(型は読み飛ばす)
  • CPU
  • python
  • 検索
  • mypy
  • 最近の Node.js も .ts の型を消して直接
  • 別に、自分で呼ぶ
  • 動かせるが、そのときは検査しない。
  • Python と同じ形
  • ひらめき
  • TS は tsc が型を調べてから JS にする。Python はそのまま実行し、検査は別に頼む。

型ヒントの書き方(TypeScript と並べて)

よく使う書き方を、TypeScript と並べました。

PythonTypeScript意味
name: strname: string文字列
n: int / x: floatn: number整数・小数(Python は分かれる)
ok: boolok: boolean真偽値
cities: list[str]cities: string[]文字列の list
temps: dict[str, float]Record<string, number>キーが str、値が float の dict
`city: str \None``city: string \null`str か None
def f(x: int = 0) -> str:function f(x = 0): string引数と戻り値
-> None: void何も返さない
Any(from typing import Any)any何でもよい=検査しない

list[str] や str | None は、Python 3.9・3.10 で入った書き方です。古い記事では List[str]・Optional[str] のように typing から import して書いていますが、Python 3.13 では今の書き方で書けます。

tenki の関数に付けると、たとえばこうなります。

python
def fetch_temp(city: str) -> float:
    ...

def forecast(cities: list[str]) -> dict[str, float]:
    return {c: fetch_temp(c) for c in cities}

def find_city(key: str) -> str | None:
    ...

型ヒントは全部に書かなくてもかまいません。まず関数の引数と戻り値に書くのが効果的です。関数の中の変数は、たいてい型チェッカーが推論(右辺から自動で判断)してくれます。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 7 / 23
  • 型ヒントの書き方(TypeScript と並べて)
  • Python
  • TypeScript
  • 意味
  • name: str
  • name: string
  • 文字列
  • n: int / x: float
  • n: number
  • 整数・小数(Python は分かれる)
  • ok: bool
  • ok: boolean
  • 真偽値
  • cities: list[str]
  • cities: string[]
  • 文字列の list
  • temps: dict[str, float]
  • Record<string, number>
  • キーが str の dict
  • city: str | None
  • city: string | null
  • str か None
  • def f(x: int = 0) -> str:
  • function f(x = 0): string
  • 引数と戻り値
  • -> None
  • : void
  • 何も返さない
  • Any(from typing)
  • any
  • 何でもよい=検査しない
  • ひらめき
  • : の後ろに型、-> の後ろに戻り値の型。None を返しうるなら str | None と書く。

mypy:実行せずに、札と使い方の食い違いを探す

mypy は、型ヒントを読んでコードを検査する道具です。Python で最も古くからある型チェッカーで、型ヒントの仕組み自体が mypy から始まりました。

python
def label(city: str, temp: int) -> str:
    return f"{city}: {temp}℃"

print(label(24, "Tokyo"))
x: int = "hello"

このファイル(hint2.py)を python で動かすと、何も言わずに 24: Tokyo℃ と出ます。mypy に渡すと、こうなります。

$ mypy hint2.py
hint2.py:4: error: Argument 1 to "label" has incompatible type "int"; expected "str"  [arg-type]
hint2.py:4: error: Argument 2 to "label" has incompatible type "str"; expected "int"  [arg-type]
hint2.py:5: error: Incompatible types in assignment (expression has type "str", variable has type "int")  [assignment]
Found 3 errors in 1 file (checked 1 source file)
出力の部分意味
hint2.py:4:ファイル名と行番号
Argument 1 to "label"label の1番目の引数が
incompatible type "int"; expected "str"int が来たが、札は str
[arg-type]エラーの種類の名前(設定で個別に止めるときに使う)

mypy はコードを1行も実行していません。だから、ネットワークに行く関数や、ファイルを消す関数でも安心して検査できます。

同じ役目の道具に pyright(パイライト。Microsoft 製)があります。VS Code の Python 拡張に入っている Pylance の中身は pyright です。同じファイルを pyright に渡しても、同じ3か所がエラーになりました。どちらを使ってもかまいませんが、プロジェクトの中では1つにそろえるのがふつうです。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 8 / 23
  • mypy:実行せずに、札と使い方の食い違いを探す
  • def label(city: str, temp: int) -> str:
  • $ mypy hint2.py # 左のファイル
  • return f"{city}: {temp}℃"
  • hint2.py:4: error: Argument 1 to "label" has
  • incompatible type "int"; expected "str"
  • print(label(24, "Tokyo"))
  • hint2.py:4: error: Argument 2 to "label" has
  • x: int = "hello"
  • incompatible type "str"; expected "int"
  • hint2.py:5: error: Incompatible types in
  • assignment (expression has type "str",
  • variable has type "int")
  • Found 3 errors in 1 file (checked 1 source file)
  • python hint2.py は何も言わずに「24: Tokyo℃」と出す。mypy は動かす前に3か所を指す
  • 同じ仕事をする道具に pyright(VS Code の Pylance の中身)もある。出すエラーはほぼ同じ
  • ひらめき
  • mypy はコードを動かさずに読むだけ。呼び出し側の間違いを、行番号つきで全部出す。

mypy が一番役に立つのは「None かもしれない」

型チェッカーが実際に一番よく助けてくれるのは、「None かもしれない値」の扱いです。

python
CITIES = {"tokyo": "東京", "osaka": "大阪"}

def find_city(key: str) -> str | None:
    return CITIES.get(key)

def shout(key: str) -> str:
    return find_city(key).upper()

dict.get は、キーが無いと None を返します(JS でオブジェクトに無いキーを読むと undefined になるのと似ています)。だから find_city の戻り値は str | None です。その結果にいきなり .upper() を呼ぶと、"nagoya" のように無いキーのとき AttributeError: 'NoneType' object has no attribute 'upper' で落ちます。

mypy は、これを実行前に教えてくれます。

$ mypy none.py
none.py:7: error: Item "None" of "str | None" has no attribute "upper"  [union-attr]

直し方は、None のときを先に片付けることです。

python
def shout(key: str) -> str:
    city = find_city(key)
    if city is None:
        return "?"
    return city.upper()   # ここでは str

if city is None: return の後では、mypy は「ここから先の city は str」と分かっています。これを型の絞り込みと呼びます。TypeScript の strictNullChecks で if (city === null) return と書くのと、まったく同じ考え方です。

第3章で is None を使う理由を見ましたが、型チェッカーも is None の形を見て絞り込みます。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 9 / 23
  • mypy が一番役に立つのは「None かもしれない」
  • CITIES = {"tokyo": "東京", "osaka": "大阪"}
  • $ mypy none.py
  • none.py:7: error: Item "None" of
  • def find_city(key: str) -> str | None:
  • "str | None" has no attribute "upper"
  • return CITIES.get(key)
  • def shout(key: str) -> str:
  • return find_city(key).upper()
  • # 直し方:None のときを先に片付ける
  • 実行すると(キーが無いとき):
  • city = find_city(key)
  • AttributeError: 'NoneType' object
  • if city is None:
  • has no attribute 'upper'
  • return "?"
  • TS の strictNullChecks と同じ考え方
  • return city.upper() # ここでは str
  • ひらめき
  • dict.get は見つからないと None を返す。その先で .upper() すると落ちることを、先に教えてくれる。

型ヒントは少しずつ足せる。ただし書いていない所は見ない

Python の型ヒントは、書いた所から少しずつ足していけます。これを段階的型付け(gradual typing)と呼びます。既存のコードに、全部を一度に書き足す必要はありません。

ただし裏返しとして、書いていない所を mypy は見ません。

python
def untyped(x):        # 型ヒントなし
    return x + 1

untyped("a")           # 実行すると TypeError
$ mypy gradual.py
Success: no issues found in 1 source file

実行すれば "a" + 1 で TypeError になるのに、mypy は「問題なし」と言いました。型ヒントの無い関数は、引数も戻り値も Any(何でもよい)として扱われ、既定では関数の中身も調べないからです。

--strict を付けると、この「書いていない」ことも指摘させられます。

$ mypy --strict gradual.py
gradual.py:1: error: Function is missing a type annotation  [no-untyped-def]
gradual.py:4: error: Call to untyped function "untyped" in typed context  [no-untyped-call]
状態mypy の見方
型ヒントの無い関数Any 扱い。中身も既定では調べない
r.json() の結果Any。外から来た JSON の形は mypy には分からない
--strict書き忘れ・Any を返すことも、エラーとして出す

tenki の weather.py で return r.json()["temp"] と書いた所を --strict で検査すると、「float を返すはずの関数が Any を返している」と言われました。外から来るデータの形は、型ヒントだけでは保証できないのです。第17章では、FastAPI(と、その中の pydantic)が、この「外から来たデータ」を実行時に確かめる役を担います。

mypy が何も言わない=正しい、ではないことを覚えておきましょう。新しく書くプロジェクトなら、最初から strict = true にしておくのが楽です。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 10 / 23
  • 型ヒントは少しずつ足せる。ただし書いていない所は見ない
  • def untyped(x): # 型ヒントなし
  • $ mypy gradual.py
  • return x + 1
  • Success: no issues found in 1 source file
  • $ mypy --strict gradual.py
  • untyped("a") # 実行すると TypeError
  • gradual.py:1: error: Function is missing a
  • type annotation [no-untyped-def]
  • gradual.py:4: error: Call to untyped function
  • "untyped" in typed context [no-untyped-call]
  • 状態
  • mypy の見方
  • 型ヒントが無い関数
  • 引数も戻り値も Any(何でもよい)扱い。中身も既定では調べない
  • r.json() の結果
  • Any。外から来た JSON の形は、mypy には分からない
  • --strict
  • 型ヒントの書き忘れや Any を返すことも、エラーとして出す
  • ひらめき
  • 型ヒントの無い関数の中身を、mypy は既定では調べない。--strict で「書いていない」も指摘させる。

ruff check:型ではなく「よくある書き間違い」を探す

型とは別に、「動くけれど、たぶん間違い」「読みにくい」書き方を探す道具をリンターと呼びます。JS でいう ESLint です。今の Python では ruff(ラフ)がよく使われます。uv と同じ Astral という会社が作っていて、Rust で書かれているのでとても速い道具です。

図の左のような、少し雑なファイルを検査してみます。

$ ruff check --output-format concise messy.py
messy.py:1:8: F401 [*] `os` imported but unused
messy.py:2:8: F401 [*] `sys` imported but unused
messy.py:3:8: F401 [*] `json` imported but unused
messy.py:6:16: E711 Comparison to `None` should be `cond is None`
Found 4 errors.
[*] 3 fixable with the `--fix` option (1 hidden fix can be enabled with the `--unsafe-fixes` option).
番号意味なぜ問題か
F401import したのに使っていない第8章のとおり import はファイルを実行するので、無駄に遅くなる。消し忘れの印でもある
E711== None で比べているNone との比較は is None で(第3章)

[*] の付いたものは、ruff check --fix で自動で直せます。E711 は、== を is に変えると意味が変わりうるので、自動では直さない(--unsafe-fixes を付けたときだけ直す)扱いになっています。

F401・E711 のような番号は、規則の名前です。Python には昔から flake8・isort・pyupgrade など、役割ごとに別々のリンターがあり、それぞれが規則に番号を付けていました。ruff はそれらの規則を1つの道具で引き継いでいるので、番号の頭の文字(F は pyflakes、E は pycodestyle など)が元の道具を表しています。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 11 / 23
  • ruff check:型ではなく「よくある書き間違い」を探す
  • # messy.py
  • $ ruff check --output-format concise messy.py
  • import os
  • messy.py:1:8: F401 [*] `os` imported but unused
  • import sys
  • messy.py:2:8: F401 [*] `sys` imported but unused
  • import json
  • messy.py:3:8: F401 [*] `json` imported but unused
  • messy.py:6:16: E711 Comparison to `None` should
  • def show(city,temp):
  • be `cond is None`
  • if temp == None:
  • Found 4 errors.
  • return city+": ?"
  • [*] 3 fixable with the `--fix` option
  • return f"{city}: {temp}℃"
  • print( show('Tokyo',24) )
  • F401 や E711 は規則の番号。昔の flake8 などの
  • 道具の規則を、ruff が1つで引き継いでいる
  • ひらめき
  • 使っていない import、== None などを指摘する。多くは --fix で自動で直せる。ESLint の役。

ruff format:書き方の揺れを1通りにそろえる

ruff にはもう1つ、フォーマッター(整形する道具)の役目があります。JS でいう Prettier です。空白・引用符・空行などを、決まった1通りの形に書き直します。

--diff を付けると、書き換えずに「どう変えるか」だけを見せてくれます。

$ ruff format --diff messy.py
-def show(city,temp):
+
+def show(city, temp):
     if temp == None:
-        return city+": ?"
+        return city + ": ?"
     return f"{city}: {temp}℃"
-print( show('Tokyo',24) )
+
+
+print(show("Tokyo", 24))
直ったもの決まり
city,tempカンマの後に空白1つ
city+": ?"演算子の前後に空白
'Tokyo'引用符は " にそろえる
関数の前後空行2行(PEP 8 という Python の書き方の指針どおり)

== None は直していないことに注目してください。フォーマッターは形だけを変え、意味は変えないのが約束です。意味にかかわる指摘は ruff check の仕事です。

ruff format の形は、Python でよく使われてきた black というフォーマッターとほぼ同じになるように作られています。形を1つに決めてしまうと、「インデントは」「引用符は」という議論が要らなくなり、差分も読みやすくなります。エディタで保存のたびに自動で整形するようにしておくのがおすすめです。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 12 / 23
  • ruff format:書き方の揺れを1通りにそろえる
  • $ ruff format --diff messy.py
  • -def show(city,temp):
  • 直ったもの
  • 決まり
  • +
  • +def show(city, temp):
  • 空白
  • カンマの後に1つ
  • if temp == None:
  • - return city+": ?"
  • 演算子
  • 前後に空白
  • + return city + ": ?"
  • -print( show('Tokyo',24) )
  • 引用符
  • " にそろえる
  • +
  • +
  • 空行
  • 関数の前後に2行
  • +print(show("Tokyo", 24))
  • == None は直さない。
  • 形だけを変え、意味は変えない
  • のが format
  • ひらめき
  • 空白・引用符・空行を決まった形に直す。Prettier の役。チームで議論しなくて済む。

pytest:ファイル名と関数名の決まりでテストを見つける

pytest は、Python で最もよく使われるテストの道具です。JS でいう Jest や Vitest にあたります。

pytest の特徴は、テストを登録しなくてよいことです。決まった名前のファイルと関数を、pytest が自分で探して全部呼びます。

決まり例
ファイル名が test_*.py か *_test.pytest_convert.py
その中の、test で始まる関数def test_freezing():
Test で始まるクラスの中の、test で始まるメソッドclass TestConvert: の test_minus

逆に、図の helper メソッド(test で始まらない)や、helpers.py の中の関数(ファイル名が決まりに合わない)は、テストとして集められません。実際に、何が集められるかを確かめられます。

$ uv run pytest --collect-only -q
tests/test_class.py::TestConvert::test_minus
tests/test_convert.py::test_freezing
tests/test_convert.py::test_boiling
tests/test_convert.py::test_body

4 tests collected in 0.01s

helpers.py の中の test_not_collected は、名前が test で始まっていても、ファイル名が test_ で始まっていないので無視されました。

この「名前の決まりで探す」は、JS の Jest が *.test.js や *.spec.ts を探すのと同じ発想です。pytest は、どのフォルダから探し始めるかも決められます(後で出てくる testpaths の設定)。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 13 / 23
  • pytest:ファイル名と関数名の決まりでテストを見つける
  • tenki/tests/
  • OK
  • ファイル名が test_*.py か *_test.py
  • test_convert.py
  • def test_freezing()
  • OK
  • その中の test で始まる関数
  • def test_boiling()
  • test_class.py
  • OK
  • Test で始まるクラスの test メソッド
  • class TestConvert:
  • NG
  • helper(test で始まらない)
  • def test_minus(self)
  • def helper(self)
  • NG
  • helpers.py の中身(ファイル名が違う)
  • helpers.py
  • def test_not_collected()
  • $ uv run pytest --collect-only -q
  • 4 tests collected
  • ひらめき
  • test_*.py の中の test で始まる関数を、自分で探して全部呼ぶ。登録の手間は無い。

書くのは assert だけ。失敗すると中身まで見せてくれる

pytest のテストは、ふつうの関数の中に `assert` 文を書くだけです。assert 条件 は Python の文で、条件が偽なら AssertionError を出します。

python
# tests/test_convert.py
from tenki.convert import to_fahrenheit

def test_freezing():
    assert to_fahrenheit(0) == 32

def test_body():
    assert to_fahrenheit(37) == 99   # わざと間違えた
$ uv run pytest
tests/test_convert.py ..F
    def test_body():
>       assert to_fahrenheit(37) == 99
E       assert 98.6 == 99
E        +  where 98.6 = to_fahrenheit(37)
FAILED tests/test_convert.py::test_body - assert 98.6 == 99
1 failed, 2 passed in 0.02s

. は成功、F は失敗です。失敗したテストでは、左辺が 98.6 だったこと、そしてそれが `to_fahrenheit(37)` の結果だったことまで表示されました。

ふつうの assert は、失敗しても「AssertionError」としか言いません。pytest は、テストのファイルを読み込むときに assert 文を書き換えて、途中の値を覚えておくようにしています。だから Jest の expect(a).toBe(b) のような専用の関数を覚えなくても、失敗の中身が分かります。

Jestpytest
等しいかexpect(a).toBe(b)assert a == b
含むかexpect(list).toContain(x)assert x in list
例外が出るかexpect(fn).toThrow()with pytest.raises(ValueError):

1行目の from tenki.convert import to_fahrenheit に注目してください。テストは、インストールされた tenki を import しているのです。これが次の問題の入口です。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 14 / 23
  • 書くのは assert だけ。失敗すると中身まで見せてくれる
  • # tests/test_convert.py
  • $ uv run pytest
  • from tenki.convert import to_fahrenheit
  • tests/test_convert.py ..F
  • def test_body():
  • def test_freezing():
  • > assert to_fahrenheit(37) == 99
  • assert to_fahrenheit(0) == 32
  • E assert 98.6 == 99
  • E + where 98.6 = to_fahrenheit(37)
  • def test_body():
  • FAILED tests/test_convert.py::test_body
  • assert to_fahrenheit(37) == 99
  • 1 failed, 2 passed in 0.02s
  • 左辺が 98.6 だったこと、それが to_fahrenheit(37) の結果だったことまで出る
  • (pytest は読み込むときに assert 文を書き換えて、途中の値を覚えさせている)
  • ひらめき
  • expect(…).toBe(…) のような専用の関数は要らない。ふつうの assert 文を pytest が読み解く。

pytest を打ったら No module named 'tenki'

この環境で、ふつうに pytest と打ったら、こうなりました。

$ pytest
E   ModuleNotFoundError: No module named 'tenki'

tenki はちゃんと .venv に入っている(第14章の .pth)のに、なぜでしょうか。

原因は、この `pytest` がどの python で動いているかです。この環境の pytest は uv tool install pytest(第14章)で入れたもので、~/.local/share/uv/tools/pytest/ という pytest 専用の venv の python で動いています。図の左です。

pytest 専用の venv(左)tenki の .venv(右)
python~/.local/share/uv/tools/pytest/bin/python/Users/you/tenki/.venv/bin/python
site-packages にあるものpytest とその依存だけtenki(.pth)・httpx・requests など
import tenki見つからない見つかる

第8章を思い出してください。import tenki は、いま動いている python の `sys.path` を探します。左の python の sys.path には、左の site-packages しかありません。右の .venv に何が入っていても、左の python からは見えないのです。

道具もただの Python のプログラムで、どれか1つの python の上で動いています。道具の「中から」見えるのは、その python に入っているものだけです。これが、この章の後半の要点です。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 15 / 23
  • pytest を打ったら No module named 'tenki'
  • ~/.local/share/uv/tools/pytest/
  • /Users/you/tenki/.venv/
  • CPU
  • python
  • site-packages/
  • site-packages/
  • pytest 専用
  • pytest/
  • CPU
  • python
  • (tenki も httpx も無い)
  • tenki 用
  • tenki_cli.pth → src/
  • httpx/ requests/
  • pytest/ mypy/ ruff
  • $ pytest # ~/.local/bin/pytest=pytest 専用の venv の python で動く
  • E ModuleNotFoundError: No module named 'tenki'
  • tenki が入っているのは右の .venv だけ
  • ひらめき
  • pytest を道具として別の venv に入れていると、その python からは tenki が見えない。

uv run pytest なら、tenki の .venv の python で動く

直し方は、pytest を tenki の `.venv` に入れて、その python で動かすことです。

bash
$ uv add --dev pytest mypy ruff
$ uv run pytest
4 passed in 0.01s

uv add --dev は、開発のときだけ使う道具を .venv に入れ、pyproject.toml の [dependency-groups] の dev に記録します(JS の npm install -D が devDependencies に書くのと同じ)。

uv run pytest は、.venv/bin/pytest を .venv の python で動かします。図の右です。この python の site-packages には、第14章で見た tenki_cli.pth があるので、sys.path に src/ が足され、import tenki が見つかります。

打つもの動く pythontenki が見えるか
pytest(uv tool で入れたもの)pytest 専用の venv見えない
uv run pytesttenki の .venv見える
.venv/bin/python -m pytesttenki の .venv見える
activate してから pytesttenki の .venv(PATH の先頭、第12章)見える

pytest の公式の手引きも、テストするプロジェクトを pip install -e . で入れてからテストすることを勧めています。uv sync や uv run は、自分のプロジェクトを自動で編集可能の形で入れてくれる(第14章)ので、何もしなくてもこの形になっています。

uv run は、JS の npx や npm run と同じ役目です。「このプロジェクトに入っている道具を、このプロジェクトの環境で動かす」。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 16 / 23
  • uv run pytest なら、tenki の .venv の python で動く
  • ~/.local/share/uv/tools/pytest/
  • /Users/you/tenki/.venv/
  • CPU
  • python
  • site-packages/
  • site-packages/
  • pytest 専用
  • pytest/
  • CPU
  • python
  • (tenki も httpx も無い)
  • tenki 用
  • tenki_cli.pth → src/
  • httpx/ requests/
  • pytest/ mypy/ ruff
  • $ uv add --dev pytest mypy ruff # .venv に入れ、[dependency-groups] に記録
  • $ uv run pytest # .venv/bin/pytest を .venv の python で
  • 4 passed in 0.01s
  • ひらめき
  • 開発の道具は dev 依存として .venv に入れ、uv run で呼ぶ。tenki は .pth で見える(第14章)。

mypy も同じ:どの python で動くかで結果が変わる

mypy でも、同じことが起きます。tenki に、httpx(第16章で使う HTTP のライブラリ)を import する net.py を足して検査しました。

$ head -1 $(which mypy)
#!/…/.local/share/uv/tools/mypy/bin/python
$ mypy src/tenki/net.py
src/tenki/net.py:1: error: Cannot find implementation or library stub for module named "httpx"  [import-not-found]

$ uv run mypy src/tenki/net.py
Success: no issues found in 1 source file

1行目の head -1 は、mypy コマンドの実行ファイルの1行目(第14章で見た #! の行)を表示しています。この mypy は、mypy 専用の venv の python で動いていることが分かります。

mypy の公式の説明によると、mypy は「自分が動いている Python の環境」に入っているパッケージから、型の情報を探します。左の venv には httpx が無いので「見つからない」になりました。右の .venv で動かせば、httpx の型の情報を読めるので「問題なし」です。

直し方やっていること
uv run mypymypy 自体を .venv の python で動かす
mypy --python-executable .venv/bin/pythonmypy は別の場所で動くが、パッケージは .venv の python から探す

ライブラリが型の情報を持っている印は、パッケージのフォルダにある `py.typed` という空のファイルです。持っていないライブラリには、types-requests のようなスタブ(型の情報だけのパッケージ)が別に配られていることがあります。mypy が「Library stubs not installed」と言うのは、この「型の情報だけのパッケージを入れて」という意味です。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 17 / 23
  • mypy も同じ:どの python で動くかで結果が変わる
  • ~/.local/share/uv/tools/mypy/
  • /Users/you/tenki/.venv/
  • CPU
  • python
  • site-packages/
  • site-packages/
  • mypy 専用
  • mypy/
  • CPU
  • python
  • (tenki も httpx も無い)
  • tenki 用
  • tenki_cli.pth → src/
  • httpx/ requests/
  • pytest/ mypy/ ruff
  • $ head -1 $(which mypy)
  • #!/…/.local/share/uv/tools/mypy/bin/python
  • $ uv run mypy src/tenki/net.py
  • $ mypy src/tenki/net.py
  • Success: no issues found in 1 source file
  • error: Cannot find implementation or
  • $ mypy --python-executable .venv/bin/python \
  • library stub for module named "httpx"
  • src/tenki/net.py
  • Success: no issues found in 1 source file
  • ひらめき
  • mypy は「自分が動いている python」の site-packages から、ライブラリの型を探す。

道具の設定も pyproject.toml の [tool.…] に集める

JS では、package.json のほかに .eslintrc・.prettierrc・jest.config.js と、道具ごとに設定のファイルが増えがちです。Python では、これらを `pyproject.toml` の `[tool.道具の名前]` に集めるのがふつうです(第14章で見た [tool.uv.build-backend] と同じ仕組み)。

toml
[dependency-groups]
dev = [
    "mypy>=2.3.1",
    "pytest>=9.1.1",
    "ruff>=0.16.9",
]

[tool.mypy]
strict = true

[tool.ruff.lint]
select = ["E", "F", "I", "UP"]

[tool.pytest.ini_options]
testpaths = ["tests"]
表意味
[dependency-groups]開発のときだけ要るもの。wheel の依存(Requires-Dist)には入らないので、tenki-cli を入れた人の手元には pytest は入らない
[tool.mypy]strict = true で、--strict を毎回付けたのと同じになる
[tool.ruff.lint]使う規則の組。E と F(基本)、I(import の並べ方)、UP(古い書き方を新しく)
[tool.pytest.ini_options]テストを tests/ から探す

[dependency-groups] の版は、uv add --dev がそのとき入れた版を書いたものです(2026年9月に実際に入った版)。

これで、友だちが tenki を clone して uv sync すれば、同じ版の mypy・ruff・pytest が .venv に入り、同じ設定で同じ検査ができます。道具も依存の一部として記録するのが、「自分のところでは通るのに」を減らすコツです。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 18 / 23
  • 道具の設定も pyproject.toml の [tool.…] に集める
  • [dependency-groups]
  • dev = [
  • 表
  • 意味
  • "mypy>=2.3.1",
  • "pytest>=9.1.1",
  • [dependency-groups]
  • 開発だけ(wheel に入らない)
  • "ruff>=0.16.9",
  • ]
  • [tool.mypy]
  • strict で厳しく検査
  • [tool.mypy]
  • [tool.ruff.lint]
  • 使う規則の組を選ぶ
  • strict = true
  • [tool.pytest…]
  • テストを探す場所
  • [tool.ruff.lint]
  • select = ["E", "F", "I", "UP"]
  • JS の devDependencies と、.eslintrc・
  • [tool.pytest.ini_options]
  • testpaths = ["tests"]
  • .prettierrc・jest.config を1つにまとめた形
  • ひらめき
  • dev 依存は [dependency-groups]、道具の設定はそれぞれの [tool.名前]。1つのファイルで済む。

エディタも「どの python を見るか」を選んでいる

エディタも同じ話です。VS Code の Python 拡張は、Pylance(中身は pyright)を使って、補完や赤い波線を出しています。そのとき、どの python の site-packages を見るかを、エディタの設定で選んでいます。これが「インタプリタの選択」(Python: Select Interpreter)です。

違う python を選んでいると、import の行にこう波線が出ます。

Import "httpx" could not be resolved

これは、pyright を実際に動かして確かめられます。同じ net.py を、.venv を向けずに検査すると上のエラーが出て、--pythonpath .venv/bin/python で .venv の python を向けると、エラーは0件になりました。

選んでいる python起きること
システムの python(左)httpx も tenki も見えず、import に波線。補完も効かない
tenki の .venv(右)波線が消え、httpx の関数の補完や説明が出る

選んだ python は、エディタの中で実行するときにも使われます。VS Code の公式の説明では、選んだ環境は「コードの実行・デバッグ・補完などの言語機能」のすべてに使われ、新しく開いたターミナルでも自動で activate されます。

つまり「インタプリタの選択」は、第12章の activate を、エディタ全体に対してするスイッチです。コードに問題が無いのに赤い波線が消えないときは、まずここを疑います。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 19 / 23
  • エディタも「どの python を見るか」を選んでいる
  • /usr/bin/python3(違う python)
  • /Users/you/tenki/.venv/
  • CPU
  • python
  • site-packages/
  • CPU
  • python
  • site-packages/
  • システムの
  • (tenki も httpx も無い)
  • tenki 用
  • tenki_cli.pth → src/
  • httpx/ requests/
  • pytest/ mypy/ ruff
  • 違う python を選ぶと
  • 画面
  • VS Code(Pylance)
  • .venv を選べば、波線が消えて
  • Import "httpx" could not be resolved
  • Python: Select Interpreter
  • httpx の補完も効く
  • Pylance の中身は pyright。同じ pyright を .venv に向けたときと向けないときで、実際にこの差が出た
  • ひらめき
  • VS Code の「インタプリタの選択」は、補完・波線・実行に使う python(と venv)を決めるスイッチ。

「どの python?」を確かめる3つの場所

迷ったときは、図の3か所が同じ `.venv` を指しているかを確かめます。

順番見る場所どう見えるか
1VS Code のステータスバー(右下)選んでいる Python の版と、venv の名前
2エディタの中のターミナルwhich python(Windows は where python)の結果
3コードの中import sys; print(sys.executable) の結果
bash
$ which python
/Users/you/tenki/.venv/bin/python
$ python -c "import sys; print(sys.executable)"
/Users/you/tenki/.venv/bin/python

3つがそろっていれば、エディタの波線・ターミナルでの実行・uv run が、すべて同じ python の上で動いています。

いくつかのコツがあります。VS Code は、開いたフォルダ(ワークスペース)の一番上にある .venv を最初の候補にします。だから tenki/ フォルダそのものを開くのが一番確実です。親のフォルダを開いて、その中に tenki が入っている形だと、.venv が見つからないことがあります。

選び直すときは、コマンドパレット(Ctrl+Shift+P、macOS は Cmd+Shift+P)で「Python: Select Interpreter」を選び、一覧から .venv を選びます。一覧に無ければ「インタプリタのパスを入力」で .venv/bin/python を指定します。

第18章の「困ったとき」でも、この3か所はもう一度出てきます。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 20 / 23
  • 「どの python?」を確かめる3つの場所
  • 画面
  • ステータスバー
  • ターミナル
  • エディタのターミナル
  • コード
  • コードの中
  • 右下の版と venv の名前
  • which python(Windows は where)
  • sys.executable を表示
  • $ which python
  • /Users/you/tenki/.venv/bin/python
  • $ python -c "import sys; print(sys.executable)"
  • /Users/you/tenki/.venv/bin/python
  • VS Code はワークスペースの一番上にある .venv を最初に候補にする。tenki/ をフォルダとして開くのが一番確実
  • 選び直すときは Ctrl+Shift+P(macOS は Cmd+Shift+P)→「Python: Select Interpreter」
  • ひらめき
  • 迷ったら、ステータスバー・ターミナル・sys.executable の3つが同じ .venv を指しているかを見る。

JS の道具でいうと

この章の道具を、JS・TypeScript の道具と並べます。

役割JS / TSPython
型の検査tsc(--noEmit)mypy・pyright
書き間違いを探すESLintruff check
形をそろえるPrettierruff format
テストJest・Vitestpytest
テストの見つけ方*.test.js・*.spec.tstest_*.py・test で始まる関数
比べ方expect(a).toBe(b)assert a == b
開発だけの依存devDependencies[dependency-groups] の dev
プロジェクトの道具で動かすnpx / npm runuv run
エディタの型の表示VS Code 内蔵の TypeScriptPylance(pyright)

ほぼ1対1ですが、大きな違いが2つあります。

1つは、型の検査が「実行への段階」に組み込まれていないことです。TypeScript は tsc を通さないと(あるいは型を消さないと)動かせませんが、Python は mypy を一度も呼ばなくても動きます。だから、CI(GitHub Actions など)で uv run mypy . を必ず回すようにしておかないと、型ヒントは「書いてあるだけ」になります。

もう1つは、道具が「どの python で動くか」を意識する必要があることです。JS の道具も node_modules の中にありますが、node は1つのプロセスでそのプロジェクトの node_modules を探すので、「道具からプロジェクトのライブラリが見えない」ことはあまり起きません。Python では、道具も python の上で動くプログラムで、その python の sys.path しか見えません。uv run で動かす習慣をつければ、この違いはほとんど気にならなくなります。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 21 / 23
  • JS の道具でいうと
  • 役割
  • JS / TS
  • Python
  • 型の検査
  • tsc(--noEmit)
  • mypy・pyright
  • 書き間違い
  • ESLint
  • ruff check
  • 形をそろえる
  • Prettier
  • ruff format
  • テスト
  • Jest・Vitest
  • pytest
  • テストの見つけ方
  • *.test.js・*.spec.ts
  • test_*.py・test で始まる関数
  • 比べ方
  • expect(a).toBe(b)
  • assert a == b
  • 開発だけの依存
  • devDependencies
  • [dependency-groups] dev
  • プロジェクトの道具で動かす
  • npx / npm run
  • uv run
  • ひらめき
  • tsc・ESLint・Prettier・Jest の4役を、mypy・ruff check・ruff format・pytest が受け持つ。

よくある間違いと、その原因

この章の内容で、よく出会うものをまとめました。

出たもの・起きたこと原因直し方
int と書いたのに str が入って動いた型ヒントは実行時に何もしないmypy を回す
mypy は通るのに、実行で TypeError型ヒントの無い関数は調べないstrict = true にする
pytest で No module named 'tenki'pytest が別の venv で動いているuv run pytest
mypy で Cannot find implementation or library stubmypy が別の venv で動いているuv run mypy
mypy で Library stubs not installed for "xxx"ライブラリに型の情報が無いtypes-xxx を dev に入れる
エディタで Import "httpx" could not be resolvedエディタが違う python を見ているSelect Interpreter で .venv
pytest が「0 tests」ファイル名が test_ で始まっていない名前の決まりに合わせる
ruff が import の順番に文句を言う規則 I(isort 相当)を選んでいるruff check --fix

こうして並べると、原因は大きく2つに分かれます。

原因当てはまるもの
型ヒントを信じすぎた1・2行目
道具やエディタが、違う python で動いている3〜6行目

後者は、どれも第8章の sys.path の話に帰ってきます。「このコマンドは、どの python で動いている? その python の site-packages に、目当てのものは入っている?」と2つ問えば、たいてい原因が見つかります。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 22 / 23
  • よくある間違いと、その原因
  • 出たもの・起きたこと
  • 原因
  • int と書いたのに str が入って動いた
  • 型ヒントは実行時に何もしない
  • mypy が何も言わないのに実行で落ちる
  • 型ヒントの無い関数は調べない(--strict)
  • pytest で No module named 'tenki'
  • pytest が別の venv で動いている
  • mypy で library stub not installed
  • mypy から見えない・型情報が無い
  • Import "httpx" could not be resolved
  • エディタが違う python を見ている
  • テストが 0 件と言われる
  • ファイル名が test_ で始まっていない
  • ruff が import の順番に文句
  • 規則 I(isort 相当)を選んでいる
  • ひらめき
  • 型ヒントを信じすぎる、道具が違う python で動いている。この2つがほとんど。

まとめ:型ヒントは札、道具は検査員、どれも「どこかの python」

言葉一言で
型ヒント作者の説明。python は実行時に読み飛ばし、__annotations__ に残すだけ
mypy・pyright実行せずに読み、型ヒントとの食い違いを出す。tsc の検査の役
`str \None` と絞り込みNone の扱い忘れを実行前に見つける。一番役に立つ場面
ruff check書き間違い・使っていない import を探す。ESLint の役
ruff format書き方を1通りにそろえる。Prettier の役
pytesttest_*.py の test 関数を探して呼ぶ。assert だけで書ける
uv run道具を .venv の python で動かす。tenki もライブラリも見える
インタプリタの選択エディタが見る python を決める。.venv を選ぶ

これで tenki は、型ヒントで説明を書き、mypy で食い違いを探し、ruff で形をそろえ、pytest で動作を確かめてから、第14章の手順で配れるようになりました。

次は、JS から来た人が一番つまずくところです。JS では、fetch は Promise を返し、await で待つのが当たり前で、裏ではいつもイベントループが回っています。Python にも async と await がありますが、Python は既定では1行ずつ待ちながら進み、イベントループは自分で作らないと存在しません。同じ await という言葉が、何を意味しているのか。第16章で見ていきます。

  • JS から来た人の Python 15 ― 型ヒントと道具
  • 23 / 23
  • まとめ:型ヒントは札、道具は検査員、どれも「どこかの python」
  • 言葉
  • 一言で
  • 型ヒント
  • 作者の説明。python は実行時に読み飛ばす(__annotations__ に残る)
  • mypy・pyright
  • 実行せずに読み、型ヒントとの食い違いを出す。tsc の検査の役
  • ruff check
  • 書き間違い・使っていない import を探す。ESLint の役
  • ruff format
  • 書き方を1通りにそろえる。Prettier の役
  • pytest
  • test_*.py の test 関数を探して呼ぶ。assert だけで書ける
  • uv run
  • 道具を .venv の python で動かす。tenki もライブラリも見える
  • インタプリタの選択
  • エディタが見る python を決める。.venv を選ぶ
  • 次の疑問:JS の await と Python の await は、同じもの?
  • ひらめき
  • 次は JS と一番違うところ。非同期の async/await とイベントループ(第16章)。

0 件のコメント

読み込み中…

権利侵害・不適切な内容の連絡: [email protected]