9.1 ソート — sorted, sort, operator
データの並べ替え(ソート)は、プログラミングにおいて最も頻繁に行われる操作の一つです。Pythonには、強力で高速、かつ使いやすいソート機能が標準で備わっています。
本記事では、「9.1 ソート」として、Pythonにおけるソートの基本である sorted() 関数と list.sort() メソッドの違いから、operator モジュールを活用した実践的なソート手法までを詳しく解説します。
1. 2つのソート方法:sorted() と sort()
Section titled “1. 2つのソート方法:sorted() と sort()”Pythonでリストなどのデータをソートする方法には、大きく分けて2種類あります。目的によって使い分けましょう。
1.1 新しいリストを作る非破壊的ソート:sorted() 関数
Section titled “1.1 新しいリストを作る非破壊的ソート:sorted() 関数”sorted() は組み込み関数であり、引数に渡したデータ(イテラブル)を並べ替えた新しいリストを返します。元のデータは変更されません。
numbers = [5, 2, 9, 1, 5]# sorted()を使用sorted_numbers = sorted(numbers)
print("元のリスト:", numbers) # [5, 2, 9, 1, 5] (変化なし)print("ソート後:", sorted_numbers) # [1, 2, 5, 5, 9] (新しいリスト)リストだけでなく、タプルや辞書のキー、文字列などもソートしてリストとして返すことができます。
1.2 元のリストを直接書き換える破壊的ソート:list.sort() メソッド
Section titled “1.2 元のリストを直接書き換える破壊的ソート:list.sort() メソッド”リスト型のメソッドである .sort() は、そのリスト自身の中身を直接並べ替えます。新しいリストを作らないためメモリ効率が良いですが、元の順番は失われます。戻り値は None です。
numbers = [5, 2, 9, 1, 5]# メソッドを実行(戻り値はNoneなので変数に代入しない)numbers.sort()
print("元のリスト:", numbers) # [1, 2, 5, 5, 9] (中身が書き換わっている)2. 降順(大きい順)への並べ替え:reverse 引数
Section titled “2. 降順(大きい順)への並べ替え:reverse 引数”どちらの方法でも、デフォルトでは昇順(小さい順、アルファベット順)にソートされます。
降順(大きい順、逆順)にしたい場合は、reverse=True というキーワード引数を指定します。
words = ["banana", "apple", "cherry"]
# アルファベットの逆順(降順)にソートdesc_words = sorted(words, reverse=True)print(desc_words) # ['cherry', 'banana', 'apple']3. ソートの基準をカスタマイズする:key 引数
Section titled “3. ソートの基準をカスタマイズする:key 引数”要素を比較する「基準」を変更したい場合は、key 引数に「1つの引数を受け取り、比較に使う値を返す関数」を指定します。
words = ["banana", "apple", "cherry", "kiwi"]
# 単語の「長さ(len)」を基準にソートするsorted_by_len = sorted(words, key=len)print(sorted_by_len) # ['kiwi', 'apple', 'banana', 'cherry']4. operator モジュールによる高度なソート
Section titled “4. operator モジュールによる高度なソート”辞書のリストや、カスタムオブジェクトのリストを特定のキーや属性でソートする場合、key 引数にラムダ式(lambda x: x['age']など)を渡すこともできますが、標準ライブラリの operator モジュールを使うと、より高速かつ可読性高く記述できます。
4.1 辞書のリストをソートする:operator.itemgetter
Section titled “4.1 辞書のリストをソートする:operator.itemgetter”リストの中に辞書やタプルが入っている場合、特定の値でソートするには itemgetter を使います。
from operator import itemgetter
users = [ {"name": "Alice", "age": 25, "score": 85}, {"name": "Bob", "age": 20, "score": 90}, {"name": "Carol", "age": 30, "score": 85}]
# 'age'(年齢)を基準にソートusers_by_age = sorted(users, key=itemgetter('age'))print(users_by_age[0]) # {'name': 'Bob', 'age': 20, 'score': 90}
# 複数のキーを指定することも可能(先にscoreで比較し、同点ならageで比較など)# key=itemgetter('score', 'age')4.2 オブジェクトのリストをソートする:operator.attrgetter
Section titled “4.2 オブジェクトのリストをソートする:operator.attrgetter”自作クラスのインスタンス(オブジェクト)が格納されたリストを、特定の属性(プロパティ)を基準にソートするには attrgetter を使います。
from operator import attrgetter
class User: def __init__(self, name, age): self.name = name self.age = age def __repr__(self): return f"{self.name}({self.age})"
user_objects = [User("Alice", 25), User("Bob", 20), User("Carol", 30)]
# 'age'属性を基準にソートsorted_users = sorted(user_objects, key=attrgetter('age'))print(sorted_users) # [Bob(20), Alice(25), Carol(30)]5. 安定ソート (Stable Sort)
Section titled “5. 安定ソート (Stable Sort)”Pythonのソートアルゴリズム(Timsort)は「安定(Stable)」であることが保証されています。 これは、「ソートの基準値が同じだった場合、ソート前の順番がそのまま維持される」という性質です。
たとえば、「スコア順」でソートした後に「年齢順」でソートすると、「同じ年齢の人の中では、スコア順に並んでいる」という状態を作ることができます。複数条件のソートを行う際に非常に役立つ特性です。