エムオーテックス株式会社が運営するテックブログです。

Python の型付けとどう付き合うか考える 〜型チェックができないのにどうして Python を選ぶの?〜

はじめに

こんにちは!テクニカルサポート課の蓑星です。

突然ですが、皆さんは Python は好きですか?私は大好きです!
弊社の製品「LANSCOPE EM クラウド版」では、Scala をメインのプログラミング言語として採用していますが、作業用スクリプトや業務支援ツールに Python を活用しています。

特に開発規模が大きくなってくると、「型」に関して悩むことは多いと思います。
Python の型の扱い方をおさらいしながら、改めて「Pythonを選ぶ理由」について見直していきたいと思います。

Python は「動的型付き言語」

Python は「書きやすさ」と「ライブラリの充実による汎用性」から人気を博しているプログラミング言語ですが、 「書きやすさ」の点に大きく貢献している仕組みの一つが「動的型付け」だと考えています。

「動的型付け」という仕組みは、変数や引数・返り値などの値の型をプログラムが実行されるまで決めておらず、 実行時に初めて実際に入れられた値によって型が決まるというものです。 対して、型がプログラムが実行される前に決まっており、決められた方の値しか入らない仕組みを「静的型付け」と呼びます。

実際に以下のような Python のコードを実行して、どんな動きをするか確かめてみましょう。
※ type() は引数の型を取得するメソッドです

a = 123
print(a)
print(type(a))

a = "abc"
print(a)
print(type(a))

a = [123, "abc"]
print(a)
print(type(a))

実行すると、このような出力になります。

123
<class 'int'>
abc
<class 'str'>
[123, 'abc']
<class 'list'>

a という同じ変数に数字でも文字列でも配列でも値が入って、入るたびに変数の型が intstrlist と変わっています。
これが、Python が動的型付き言語だということの証明ですね。

コーディングの段階では変数に入る型が決まっておらず様々な型を受け入れることができるため、より柔軟だといえます。
この特徴が特に役立つ場面として、API 利用など外部との連携を行うときが挙げられます。
レスポンスデータが不定だったり、独自の定義をされている場合などに自分が利用しやすい形に解釈することがやりやすいです。
(静的型付けの場合は、レスポンスを送ってくる通りの型で一度受け止める必要があります)

動的型付け vs 静的型付け

動的型付けと静的型付けの特徴について対比する形で考えてみます。

  • コードの記述量が少ない ⇔ コードの記述量が多い
  • 型に関する制約が少なく、柔軟性が高い ⇔ 型に関する制約があり、柔軟性に乏しい
  • コードを書く際に必ずしも型を考慮する必要がない ⇔ 厳格に型を考慮してコードを書く必要がある
  • 実行時に型を判定するため、実行速度が遅い ⇔ 実行前に型を判定しているため、実行速度が速い
  • 実行するまで型エラーが検知できない ⇔ コンパイル時に型エラーが検知される

こうして並べてみると、「あれ?もしかして動的型付けって『楽に書ける』以外の利点に乏しい?」と感じるかもしれません。
「必ずしも型を考慮する必要がない」なんて煮え切らない表現をしたのも、実際のところ型ガン無視でコードを書いても必ずバグが出てしまうからです。(断言します)
変数が様々な型を受け止めることはできても、実際にその変数を使って処理が行われるときに型が合わなければ正しく処理ができないからですね。ものすごく噛み砕いて言えば、1 + 'a' は計算できないよね、ということです。
バグが出やすいということはすなわちデバッグに要する時間が延びるということで、せっかくコードを早く書き終えてもデバッグに時間をかけていたら本末転倒ですよね。

そのため、特に規模の大きい開発では静的型付き言語の方が好まれるのかな…と感じます。

それでも Python を使う理由

ここまで少しネガティブな情報に寄っていましたが、実際のところ Python は人気の高い言語として挙げられることが多いです。
なぜ人気なのでしょうか?私が実際にコーディングした上で感じたことや一般的に語られていることを踏まえて、Python を使う利点についてまとめてみました。

①制約が少ないゆえにコードの記述量が少なくできる

構文がシンプルになるように開発されているため、他の言語に比べても同じような動きをするコードでもコードの記述量が少ないです。
コードの記述量が少ないということは簡潔に書きやすいということであり、他人が書いたコードでも読みやすいというのは大きな利点です。
また、コーディングに要する時間が比較的短くなりやすいです。後述するライブラリも、コーディングの負荷軽減に貢献してくれます。

②学習コストが比較的低い

前項につながる部分ではありますが、構文がシンプルであるため初学者でも学習がしやすいです。
利用者も多くコミュニティが充実しているため、参考になる情報も数多く見つけることができます。
プログラミングに初めて挑戦する方にも、おすすめしやすいのではないでしょうか。

③幅広いライブラリによる圧倒的な汎用性

Python は非常にライブラリが充実しています。 AWS などクラウドサービスとの連携を行いたい場合にも SDK が用意されているものも多いですし、
統計や機械学習など専門性の高いものも多く公開されています。
それらを組み合わせることによって、特別な知識を必要とせずとも様々なアプリケーションを作り上げることができます。

④試行錯誤がしやすい

コンパイラを通さないということは、実行環境さえ整っていればテスト実行が非常に容易だということです。
Python で書かれたコードは、スクリプトとしてそのまま実行することができます。
ドキュメントを読み込んだり、コード例などから情報を得ることも大事ですが、実際に動かしてみることで分かることも多いと思います。
柔軟性が高く多少の無理を許してくれる、というのにも開発段階では助けられることも多いです。あまり甘えすぎると後が大変にもなりますけどね。

簡単にまとめれば、「書きやすくて柔軟だから」という点が大きいです。
ですから、エムオーテックスの開発でも作業用のスクリプトや業務支援ツールなど、時間をかけてでも品質が求められるわけではない部分には積極的に利用されているということですね。

結局のところ「プログラミング言語は適材適所」が浸透した現代ですから、欠点を差し引いてもそれ以上の利点があるから。ということにはなると思います。
ですが、欠点をなるべくカバーしながら利点を活かせれば、よりハッピーですよね。
Python の柔軟性を享受しつつも型に関するエラーをなるべく防げるよう、コーディングの段階で工夫をしていくのがいいんじゃないかなと考えています。

アノテーションを書こう

Python の型エラーを防ぐ手段の一つとして、「アノテーションをしっかり書く」というものがあります。 アノテーションとは「注釈」の意味を持つ言葉で、プログラミング言語においては「開発に必要な情報をソースコードに埋め込む」ためのものです。 Python の標準機能として「型ヒント」がそれを担っています。

docs.python.org

以下のような、単純に引数2つの + 演算を行った結果を返すメソッドがあるとします。

def hogehoge(a, b):
    return a+b

a, b がどんな型かが分からず、+ は加算だけでなく文字列結合など他の演算を行うこともあるので、これを見ただけではこのメソッドをどう扱えばいいかわかりません。
型ヒントを用いると、以下のように書くことができます。

def hogehoge(a: int, b: int) -> int:
    return a+b
def hogehoge(a: str, b: str) -> str:
    return a+b

引数と返り値の型が示されることで、前者は整数同士の加算であること・後者は文字列同士の結合であることがはっきりしましたね。
ソース上で変数の型を把握できるようになることで、初めてコードを見た人でも両者の違いに気づくことができます。
それによって、ケアレスミスや認識違いによる実行時の型エラーの軽減につながります。

また、変数やクラスのパラメータに対しても同様に書くことができます。
名前・年齢・身長(cm 単位・小数あり)のパラメータを持つ Profile クラスを定義する例をとります。
以下のように書くことで、それぞれのパラメータがどのような型を持つことを想定するかを示すことができます。

@dataclass
class Profile
    name: str
    age: int
    height: float

型ヒントを使いこなしてアノテーションを整備することで、型エラーに気づきにくいという Python の弱点をカバーしつつ読みやすさもさらに向上させることができます。
注意点として、あくまで「注釈」であるため実行時に型ヒントと違う型の数値が代入されてもエラーとして検出されることはありません。
PyCharm など一部の IDE は、型ヒントをもとに型の不一致が疑われるときに警告してくれる機能があるので、あわせて利用することをおすすめします。

pleiades.io

おわりに

プログラミング言語として Python を利用する理由を、「型」の切り口から考えていきました。
製品そのものにこそ他の言語がメインで使われるものの、エムオーテックスの開発内でも Python は重要な役割を果たしています。
複数人での開発がほとんどなので、「他人がコードを読んで理解できるか」は特に重要視されています。今回ご紹介した「アノテーション」も、開発内のコーディング規約に盛り込まれています。
それぞれの言語の特徴を理解し、欠点をカバーして利点を最大限に活かせるようコーディングしていきたいですね!