Pythonリストのソート方法完全ガイド|sort()とsorted()の違いからカスタムソートまで解説

1. はじめに

Pythonはシンプルで直感的なプログラミング言語として広く使われています。その中でも「リストのソート」は基本的かつ頻繁に利用される操作の一つです。本記事では、Pythonにおけるリストのソート方法を詳しく解説し、カスタマイズ方法や注意点についても紹介します。

この記事を読むことで、初心者の方でも効率的にリストをソートする方法を理解でき、実際のプログラムで応用できるようになるでしょう。

2. リストのソート方法

Pythonには主に2つのソート手法があります。それぞれの特徴と使い方を見ていきましょう。

2.1 sort()メソッドを使ったソート

sort()メソッドはリストオブジェクトに用意されているメソッドで、元のリストを直接並び替えます。デフォルトでは昇順(小さい順)にソートされますが、reverse=Trueを指定すると降順(大きい順)になります。

基本的な使い方

numbers = [3, 1, 4, 1, 5]
numbers.sort()
print(numbers)  # 出力: [1, 1, 3, 4, 5]

降順に並び替える場合

numbers = [3, 1, 4, 1, 5]
numbers.sort(reverse=True)
print(numbers)  # 出力: [5, 4, 3, 1, 1]

2.2 sorted()関数を使ったソート

sorted()関数は元のリストを変更せず、新しいソート済みのリストを返します。こちらもデフォルトは昇順で、reverse=Trueを使えば降順にできます。

基本的な使い方

numbers = [3, 1, 4, 1, 5]
sorted_numbers = sorted(numbers)
print(sorted_numbers)  # 出力: [1, 1, 3, 4, 5]
print(numbers)  # 出力: [3, 1, 4, 1, 5] 元のリストは変更されない

降順にソート

numbers = [3, 1, 4, 1, 5]
sorted_numbers = sorted(numbers, reverse=True)
print(sorted_numbers)  # 出力: [5, 4, 3, 1, 1]

 

3. ソートのカスタマイズ

Pythonでは、key引数を使ってカスタムソートが可能です。独自のルールでリストを並び替えたい場合に役立ちます。

3.1 key引数を使ったソート

文字列の長さでソート

リスト内の要素が文字列の場合、keylen関数を指定すると文字列の長さに基づいてソートされます。

words = ['banana', 'apple', 'cherry', 'date']
words.sort(key=len)
print(words)  # ['date', 'apple', 'banana', 'cherry']

3.2 辞書のリストをソートする

辞書を含むリストをソートする場合、key引数にlambda関数を用いることで、特定のキーに基づいてソートできます。

辞書の中の数値でソート

fruits = [
    {'name': 'apple', 'price': 100},
    {'name': 'banana', 'price': 50},
    {'name': 'cherry', 'price': 150}
]

fruits.sort(key=lambda x: x['price'])
print(fruits)
# [{'name': 'banana', 'price': 50}, {'name': 'apple', 'price': 100}, {'name': 'cherry', 'price': 150}]

複数条件でソート

価格が同じ場合は名前の順でソートしたい場合、タプルをkeyに指定します。

fruits.sort(key=lambda x: (x['price'], x['name']))
print(fruits)
# [{'name': 'banana', 'price': 50}, {'name': 'apple', 'price': 100}, {'name': 'cherry', 'price': 150}]

4. ソートの注意点

4.1 異なるデータ型が混在している場合

リストに数値と文字列が混在していると、TypeErrorが発生します。注意しましょう。

mixed_list = [3, 'apple', 2]
mixed_list.sort()
# 出力: TypeError: '<' not supported between instances of 'str' and 'int'

解決策

データ型を統一するか、カスタムソートを使用します。

mixed_list = [3, 'apple', 2]
mixed_list = [str(x) for x in mixed_list]
mixed_list.sort()
print(mixed_list)  # ['2', '3', 'apple']

4.2 ソートの安定性

Pythonのソートは「安定ソート」です。同じ値が存在する場合、元のリストの順序が保持されます。

data = [(1, 'a'), (2, 'b'), (1, 'c')]
data.sort(key=lambda x: x[0])
print(data)  # [(1, 'a'), (1, 'c'), (2, 'b')]

 

5. TimSort: Pythonのデフォルトソートアルゴリズム

Pythonでは、リストのソートにTimSort(ティムソート) というアルゴリズムが使用されています。これは、マージソート挿入ソートを組み合わせた安定なソートアルゴリズムです。

TimSortの特徴

  1. 安定ソート: 同じ値の要素の順序が維持されます。
  2. 高速な処理: 実際のデータセットに対して効率が良く、平均的なケースで高速です。
  3. 部分的にソートされたデータに強い: あらかじめ並び替えがある程度行われているデータに対して、特にパフォーマンスが向上します。

6. sort()とsorted()のパフォーマンス比較

Pythonのsort()メソッドとsorted()関数には動作上の違いがあるため、パフォーマンスについても理解しておきましょう。

基本的な違い

特徴sort()sorted()
操作対象元のリストを変更新しいリストを作成
戻り値なし(Noneを返す)新しいソート済みリスト
使用ケースリストが変更されても良い場合元のリストを保持したい場合

パフォーマンスの比較

sort()の方がsorted()より若干高速です。なぜなら、sort()はリストを直接並び替えるため、メモリの追加コストが発生しないからです。

ベンチマーク例

import time

numbers = [3, 1, 4, 1, 5] * 100000

# sort()の計測
start = time.time()
numbers.sort()
end = time.time()
print("sort()の処理時間:", end - start)

# sorted()の計測
numbers = [3, 1, 4, 1, 5] * 100000
start = time.time()
sorted_numbers = sorted(numbers)
end = time.time()
print("sorted()の処理時間:", end - start)

このように、sort()sorted()は使用ケースによって適切に選択することが重要です。

7. リストのソートに役立つテクニック

逆順ソートの簡単な書き方

sort()sorted()を使わずに、リストを逆順に並べる方法もあります。

numbers = [1, 2, 3, 4, 5]
reverse_numbers = numbers[::-1]
print(reverse_numbers)  # 出力: [5, 4, 3, 2, 1]

8. よくあるエラーと解決策TypeError: ‘<‘ not supported between instances

  • 原因: リストに異なるデータ型(例: 数値と文字列)が含まれている場合。
  • 解決策: データ型を統一するか、カスタムソートで明示的に処理します。

# エラーが発生するコード
mixed_list = [3, 'apple', 2]
mixed_list.sort()  # TypeError

# 解決策
mixed_list = [str(x) for x in mixed_list]
mixed_list.sort()
print(mixed_list)  # ['2', '3', 'apple']

9. よくある質問(FAQ)

Q1: sort()sorted()の違いは何ですか?

A:

  • sort()メソッド: リストの内容を直接並び替えます。元のリストが変更されます。
  • sorted()関数: 新しいソート済みのリストを返します。元のリストは変更されません。

numbers = [3, 1, 4, 1, 5]

# sort()を使う場合
numbers.sort()
print(numbers)  # [1, 1, 3, 4, 5] - 元のリストが変更される

# sorted()を使う場合
numbers = [3, 1, 4, 1, 5]
sorted_numbers = sorted(numbers)
print(sorted_numbers)  # [1, 1, 3, 4, 5]
print(numbers)  # [3, 1, 4, 1, 5] - 元のリストは変更されない

Q2: 降順にソートするにはどうすれば良いですか?

A:
sort()メソッドやsorted()関数にreverse=Trueを指定することで降順にソートできます。

numbers = [3, 1, 4, 1, 5]

# sort()で降順にソート
numbers.sort(reverse=True)
print(numbers)  # [5, 4, 3, 1, 1]

# sorted()で降順にソート
numbers = [3, 1, 4, 1, 5]
sorted_numbers = sorted(numbers, reverse=True)
print(sorted_numbers)  # [5, 4, 3, 1, 1]

Q3: カスタムソートとは何ですか?どう使いますか?

A:
カスタムソートとは、ソートの基準を自分で定義する方法です。key引数を使って関数やlambda式を指定します。例えば、文字列の長さや辞書の特定のキーを基準にソートできます。

例: 文字列の長さでソート

words = ['banana', 'apple', 'cherry', 'date']
words.sort(key=len)
print(words)  # ['date', 'apple', 'banana', 'cherry']

Q4: Pythonのソートアルゴリズムは何ですか?

A:
PythonではTimSort(ティムソート) というソートアルゴリズムが使われています。これはマージソート挿入ソートを組み合わせたアルゴリズムで、部分的にソートされたデータに対して効率的です。

Q5: ソートが遅い場合の対処法はありますか?

A:
リストの要素数が非常に多い場合や複雑なカスタムソートを行うと、処理速度が遅くなることがあります。以下の方法で改善できます。

  1. 必要最小限のソートに絞る
    不要なソートを避け、データの一部だけをソートするように工夫します。
  2. heapqモジュールを使用する
    Python標準ライブラリのheapqを使えば、部分的なソート(例: 上位N件の取得)が効率的に行えます。

例: 上位3件の数値を取得

import heapq

numbers = [5, 1, 8, 3, 2, 9]
top3 = heapq.nlargest(3, numbers)
print(top3)  # [9, 8, 5]