Pythonの「なるほど!」と思えるユニークな機能②

こんにちは。ゆうせいです。

「Pythonic(Pythonらしい)」な書き方を追求する旅、どんどんいきましょう!

今回は、私たちが普段「数学」で使っている書き方を、そのままプログラムとして受け入れてくれるPythonの「柔軟さ」についてご紹介します。


特徴1: まるで数学の教科書!「比較演算子の連結」

クイズです。変数 x が「10より大きく、かつ20より小さい」かどうかを判定する if文 を書きたいとき、どうしますか?

多くのプログラミング言語では、こう書くのが一般的です。

x = 15
if x > 10 and x < 20:
    print("xは10と20の間にあります")

「x は 10 より大きい」 AND 「x は 20 より小さい」

このように、and(論理積)を使って二つの条件式を連結しますよね。

ところがPythonは、私たちが数学のテストで書くような、こんな書き方を許してくれます。

x = 15
if 10 < x < 20:
    print("xは10と20の間にあります")

10 < x < 20

…え、ウソでしょ!?

そう、if文 の条件式に、まるで数学の不等式をそのまま写したかのように書けてしまうんです!

専門用語解説: 比較演算子の連結 (Comparison Chaining)

これはPythonの「比較演算子の連結」と呼ばれる機能です。

10 < x < 20 と書くと、Pythonはこれを「x > 10 かつ x < 20」であると賢く解釈してくれます。

例えるなら、Pythonが数学の教科書を読んで「あ、この書き方、人間にとって自然だよね。OK、僕もこれ理解できるようにするよ!」と対応してくれたようなものです。

この機能の素晴らしい点は、「読みやすさ(可読性)」が劇的に上がることです。

x > 10 and x < 20 よりも 10 < x < 20 の方が、「x が 10 と 20 の間に挟まれている」という意図が一瞬で伝わってきませんか?

a == b == c (a と b と c が全部等しい)のような書き方も可能です。

コードを「プログラム言語」としてではなく、「人間の思考」に近い形で表現できる、非常にPythonicな特徴の一つです。


特徴2: キーが無くても怒らない!「defaultdict

続いては、Pythonの「賢い道具箱」から、最強のアイテムを一つご紹介します。

それは、辞書(dict)を扱うときの、あの面倒な「KeyError(キーエラー)」から私たちを解放してくれる魔法の辞書です。

問題:

文字列 s = "apple" の中に、各アルファベットが何個あるか数えたいとします。

皆さんは for ループと辞書を使って、こう書くかもしれません。

defaultdict を知らない場合:

counts = {} # 空の辞書を用意
s = "apple"

for char in s:
    if char in counts:
        counts[char] += 1 # すでにキーがあれば、+1
    else:
        counts[char] = 1 # キーがなければ、新しく 1 を設定
        
print(counts)
# {'a': 1, 'p': 2, 'l': 1, 'e': 1}

'a' が最初に来たとき、counts の中は空っぽです。

いきなり counts['a'] += 1 (counts['a'] に 1 を足す)を実行しようとしても、「counts['a'] なんてキーは存在しません!」と KeyError で怒られてしまいます。

だから、「if char in counts:」という確認(if文)が必ず必要になり、コードがごちゃごちゃしてしまいますよね?

ここで、collections という道具箱(モジュール)から defaultdict(デフォルトディクト)を呼び出します。

defaultdict を使った場合:

from collections import defaultdict

counts = defaultdict(int) # 「int」を初期値生成器として指定
s = "apple"

for char in s:
    counts[char] += 1 # ← if文が要らない!!
    
print(counts)
# defaultdict(<class 'int'>, {'a': 1, 'p': 2, 'l': 1, 'e': 1})

if文 が消えました!

counts = defaultdict(int) と書くだけで、KeyError が起きなくなったんです!

専門用語解説: defaultdict

defaultdict は、その名の通り「デフォルト値(初期値)」を賢く提供してくれる辞書です。

defaultdict(int) と書いたのは、「もし存在しないキーにアクセスしようとしたら、int() を呼び出して、その結果(つまり 0)を初期値として自動で設定してください」というおまじないです。

counts['a'] += 1 が実行される流れを見てみましょう。

  1. counts'a' というキーを探す。
  2. defaultdict「あ、'a' はまだ無いですね!」
  3. defaultdict「では、お約束の int() を呼びます…はい、0 が作られました」
  4. defaultdict「この 0'a' の初期値として自動で設定しますね!」(counts{'a': 0} になる)
  5. counts['a'](中身は 0)に 1 が足され、1 になる。

if文 でやっていた「キーが無ければ 0int() の結果)を入れる」という作業を、defaultdict全自動で肩代わりしてくれたのです!

例えるなら、「魔法の貯金箱」です。

普通の貯金箱(dict)は、入っていない種類のコイン(キー)の場所を触ろうとすると「無い!」と怒ります。

defaultdict(int) は、500円玉(キー)の場所を触ったときに、もし空っぽなら、自動で 0円(int())をそこに出現させてくれる貯金箱、というイメージです。

defaultdict(list) と書けば、初期値が空のリスト [] になり、defaultdict(set) と書けば空のセット set() になります。

グループ分けや集計処理が、信じられないほどスッキリ書けるようになりますよ!


まとめと今後の学習指針

今回も、Pythonの「賢さ」が光る2つの特徴をご紹介しました。

  1. 比較演算子の連結: 10 < x < 20 のように、数学の式のように直感的に書ける!
  2. defaultdict: KeyError を恐れずに辞書を使える! if文 が減ってコードが超スッキリ!

10 < x < 20 は、Pythonの「読みやすさ(Readability)」を最重要視する哲学の表れです。

defaultdict は、プログラマーが頻繁に行う「面倒な決まり文句(if key in...)」を、賢い道具で解決しよう、というPythonの「実用性」の表れです。

Pythonは、このように「読みやすく、書くのも楽に」なるための機能をたくさん備えています。

(実は、defaultdict と似た仲間に Counter という、まさに「集計」に特化した最強の道具も collections の中に存在します!)

こうした便利な道具(標準ライブラリ)の存在を知っているかどうかで、コードの品質は劇的に変わります。

ぜひ、collections の道具箱には他に何が入っているのか、覗いてみてくださいね!

セイ・コンサルティング・グループの新人エンジニア研修のメニューへのリンク

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。