言われてみれば当たり前なんですけど、typing使い始めたばかりのころのコードで勘違いをしていたのを発見したのでメモ。
バグの原因になる可能性がありました。
参考:typing --- 型ヒントのサポート — Python 3.9.2 ドキュメント#typing.Optional
検証:Visual Studio Code, Pylance, Python 3.8.5
簡潔に
Optional
=「Nullable(Noneable)」だということ。
引数が英語で言う「optional」だという意味ではない。
例
正しい例
以下のように、引数right
にデフォルト引数を設定することを考えます。
# 引数2個を加算して返す関数(正しい例)
def add_num(left: int, right: int = 0) -> int:
added = left + right
return added
# 正しい使い方
eight = add_num(3, 5)
seven = add_num(7) # 自動的に2番目の引数に 0 が設定される
# これはおかしいので警告が出るべき
wrong = add_num(9, None) # Type "None" cannot be assigned to type "int"
最後の行で関数add_num
に不適切な引数を渡しているので、以下のように警告が表示されます。
Type "None" cannot be assigned to type "int"
間違った例
「デフォルト引数は省略可能だから、optionalだよね」と勘違いをして以下のようにOptional
指定をしてしまっていました。
from typing import Optional
# 引数2個を加算して返す関数(typingの指定がおかしい)
def add_num(left: int, right: Optional[int] = 0) -> int:
added = left + right
return added
# 正しい使い方
eight = add_num(3, 5)
seven = add_num(7) # 自動的に2番目の引数に 0 が設定される
# これは本来おかしいのだが…
wrong = add_num(9, None) # 警告は出ない
Optional
指定はあくまでも None
を渡すことができる ということを示すものなので、このような場合はOptional
指定は不適切です。
今回の例の場合は、コーディングの段階では警告は表示されません。
実行時にint
とNone
を加算しようとして例外が発生することになります。
⇒ 追記:現在(Python3.11)はint
とint | None
との加算はできない旨のエラーメッセージが出ます。当時は出てなかったはず。
不適切ではない例
正しいOptional
指定の使い方はこんな感じ。
(あくまでもOptionalの使い方の話です。なんでそこでNone
を渡すんだとかいうツッコミは無しで…)
from typing import Optional
# 引数2個を加算して返す関数(rightにNone指定が可能)
def add_num(left: int, right: Optional[int] = 0) -> int:
right_int: int = 0 # intであることが確実な変数
if right is not None:
right_int = right
added = left + right_int
return added
# 正しい使い方
eight = add_num(3, 5)
seven = add_num(7) # 自動的に2番目の引数に 0 が設定される
# 関数の仕様として意図した使い方
not_wrong = add_num(9, None) # もちろん警告は出ない
不適切ではない例その2
Python4.0でint | None
的な書き方ができるようになるらしいですが、
Python3.10でint | None
的な書き方ができるようになりました。
参考:組み込み型 — Python 3.11.0b5 ドキュメント#Union Type
from typing import Optional
# 引数2個を加算して返す関数(rightにNone指定が可能)
def add_num(left: int, right: int | None = 0) -> int:
right_int: int = 0 # intであることが確実な変数
if right is not None:
right_int = right
added = left + right_int
return added
# 正しい使い方
eight = add_num(3, 5)
seven = add_num(7) # 自動的に2番目の引数に 0 が設定される
# 関数の仕様として意図した使い方
not_wrong = add_num(9, None) # もちろん警告は出ない
Noneを引数で渡す!ということが明確になって良いですね。
int | None
的な書き方ができる環境なら、Optional
なんてわかりにくいものは使っちゃダメです。