Pythonのリストを逆順に並べたいとき、単に順番を反転したい場合と、値に基づいて降順に並べ替えたい場合があります。
本記事では初心者の方でも迷わないように、リストの反転と逆順ソートの違いを最初に整理し、3つの代表的な方法(reverse、スライス、sorted/sort)を実例つきで丁寧に解説します。
- 関連記事:リスト(list)の使い方
- 関連記事:リストを昇順・降順にソートする
リストの逆順ソート/反転とは
同じ「逆順」という言葉でも、実は2通りの操作が存在します。
反転(reverse)は要素の並びを単純にひっくり返す操作で、逆順ソート(descending sort)は値の大小に基づいて降順に並べる操作です。
前者は元の相対関係を逆向きに保ちますが、後者は値を評価して並べ替えるため並びが大きく変わる可能性があります。
以下の表で違いを押さえてから、各方法の詳細に進みます。
| 観点 | 反転(reverse) | 逆順ソート(descending sort) |
|---|---|---|
| 目的 | 並び順を逆向きにする | 値に基づき降順に整列する |
| 並べ替え根拠 | 位置のみ(値は見ない) | 値(必要に応じてkey関数) |
| 元の順序情報 | 完全に逆順として保持 | 値が同じ場合のみ相対順序を保持(安定ソート) |
| 計算量の目安 | O(n) | O(n log n)(Timsort) |
| 代表的な書き方 | list.reverse()、lst[::-1] | sorted(lst, reverse=True)、lst.sort(reverse=True) |
「逆順ソート」は値の大小を考える点で「反転」と異なることに注意してください。
方法1:list.reverse()でリストを反転
特徴(破壊的・戻り値None)
list.reverse()はリストを就地(インプレース)で反転するメソッドです。
つまり、元のリスト自体が書き換わり、新しいリストは作りません。
戻り値はNoneなので、x = lst.reverse()としてもxには何も入りません。
- 関連記事:PythonのNoneとは?意味と使い方
この仕様は、「返り値で結果を受け取らない」点において初心者がつまずきやすい部分です。
さらに細かな性質として、計算量はおおよそO(n)、メモリ増加は小さいです。
実例(数値リストの反転)
次のコードでは、反転前後と戻り値を確認します。
# 反転前後と戻り値の挙動を確認する例
nums = [3, 1, 4, 1, 5]
print("反転前:", nums)
ret = nums.reverse() # 就地(インプレース)で反転。戻り値はNone
print("戻り値:", ret)
print("反転後:", nums)
反転前: [3, 1, 4, 1, 5]
戻り値: None
反転後: [5, 1, 4, 1, 3]
注意点(元の順序に戻すには再度reverse)
元の順序に戻したい場合は、もう一度reverse()を呼べば元に戻ります。
ただし、元の順序を保持しておきたいなら先にコピーを取るのが安全です。
# reverseを2回呼ぶと元に戻る例
lst = [10, 20, 30]
print("初期:", lst)
lst.reverse()
print("1回目の反転:", lst)
lst.reverse()
print("2回目の反転(元に戻る):", lst)
# 元の順序を保持したいならコピーを作っておく
original = lst.copy()
lst.reverse() # 以降はlstを自由に反転してもoriginalは保持される
print("original:", original)
print("lst(反転後):", lst)
初期: [10, 20, 30]
1回目の反転: [30, 20, 10]
2回目の反転(元に戻る): [10, 20, 30]
original: [10, 20, 30]
lst(反転後): [30, 20, 10]
方法2:スライス[::-1]でリストを反転
特徴(新しいリストを作成)
lst[::-1]は新しいリストを生成して反転結果を返します。
元のリストは変更されません。
コピーが作られるため、メモリ消費は元のリスト分だけ増えます。
- 関連記事:リストの一部を取り出す – スライス
実例(コピーして反転)
異なるオブジェクトが作られることを確認します。
# スライスで新しい反転リストを作る例
a = [1, 2, 3, 4]
b = a[::-1] # 新しいリストを作成して反転
print("a:", a)
print("b(反転):", b)
# オブジェクトIDや同一性をチェック
print("id(a) != id(b):", id(a) != id(b))
print("a is b:", a is b) # False なら別オブジェクト
a: [1, 2, 3, 4]
b(反転): [4, 3, 2, 1]
id(a) != id(b): True
a is b: False
reversed()との違い(イテレータの扱い)
reversed(lst)は反転順に要素を返すイテレータを生成します。
新しいリストを作らないためメモリ効率が良い反面、listのようにインデックスでアクセスはできません。
必要に応じてlist()でリスト化します。
# reversed()はイテレータを返す。必要に応じてリスト化する
c = [10, 20, 30]
it = reversed(c) # 反転方向のイテレータ(一度きりの順次取り出し)
print("イテレータ:", it)
# 1) 反転順に走査
for x in it:
print("forで取り出し:", x)
# 2) 再度取り出したい場合は、再びreversed(...)を作るか、list(...)で確定させる
d = [100, 200, 300]
print("list(reversed(d)):", list(reversed(d)))
# 3) イテレータはインデックスアクセスできない
try:
it2 = reversed(d)
print(it2[0]) # エラー
except TypeError as e:
print("インデックス不可:", e)
イテレータ: <list_reverseiterator object at ...>
forで取り出し: 30
forで取り出し: 20
forで取り出し: 10
list(reversed(d)): [300, 200, 100]
インデックス不可: 'list_reverseiterator' object is not subscriptable
注意点(大きなリストはメモリ増)
lst[::-1]は全要素を複製するため、大きなリストではメモリ負荷が増えます。
大量データの処理やストリーム的な処理では、reversed(lst)で逐次処理するか、list.reverse()で就地反転を検討してください。
方法3:sorted/sort(reverse=True)で逆順ソート
特徴(降順ソートと安定ソート)
逆順ソートは値に基づく降順の並べ替えであり、単なる反転ではありません。
Pythonのsorted関数とlist.sortメソッドは安定ソートで、keyで同じ値と見なされた要素の相対順序は保持されます。
計算量は概ねO(n log n)です。
sorted(iterable, reverse=True): 新しいリストを返す(元は変更しない)lst.sort(reverse=True): 就地変更(元のリストが書き変わる)
「反転(reverse)は位置を逆にするだけ」「逆順ソートは値で並べる」という違いを意識してください。
実例(sortedで新規・sortで就地変更)
# sortedは新しいリストを返し、sortは就地変更する
nums = [3, 1, 4, 1, 5]
desc1 = sorted(nums, reverse=True) # 新しいリスト
print("sortedで降順:", desc1)
print("元のnums(不変):", nums)
nums.sort(reverse=True) # 就地変更
print("sortで降順(就地):", nums)
sortedで降順: [5, 4, 3, 1, 1]
元のnums(不変): [3, 1, 4, 1, 5]
sortで降順(就地): [5, 4, 3, 1, 1]
文字列やkey指定での逆順(key=lenなど)
文字列の辞書順(Unicode順)での降順、長さでの降順、さらに安定性(同じkeyでの相対順保持)を確認します。
# 文字列の降順と、key=lenでの降順 + 安定性を確認
words = ["pear", "apple", "banana", "fig", "kiwi"]
print("辞書順の降順:", sorted(words, reverse=True))
# 長さで降順。長さが同じものは元の相対順が保たれる(安定ソート)
words2 = ["aa", "b", "cc", "d", "ee", "f"]
by_len_desc = sorted(words2, key=len, reverse=True)
print("長さで降順(key=len):", by_len_desc)
# 安定性の確認(同じ長さの'aa','cc','ee'の相対順は保持)
print("元の順序:", words2)
辞書順の降順: ['pear', 'kiwi', 'fig', 'banana', 'apple']
長さで降順(key=len): ['aa', 'cc', 'ee', 'b', 'd', 'f']
元の順序: ['aa', 'b', 'cc', 'd', 'ee', 'f']
上の結果より、長さが同じ要素(‘aa’,’cc’,’ee’)は元の相対順(出現順)を保ったまま長い順に並ぶことが分かります。
注意点(比較できない型の混在に注意)
Python 3では、数値と文字列など比較不能な型が混在するとTypeErrorになります。
keyで比較基準をそろえるか、そもそも混在を避ける設計にしましょう。
# 比較できない型が混在するとTypeError
data = [3, "a", 1]
try:
print(sorted(data, reverse=True))
except TypeError as e:
print("エラー:", e)
# どうしても混在を並べたいなら、文字列化など共通のkeyを与える(順序の意味は変わる)
mixed_sorted = sorted(data, key=str, reverse=True)
print("key=strでの降順:", mixed_sorted)
エラー: '<' not supported between instances of 'str' and 'int'
key=strでの降順: ['a', '3', '1']
key=strのような回避策は技術的には機能しますが、比較基準が「文字列表現」になってしまうため、意図した順序とかけ離れる場合があります。
データ設計面で型をそろえるのが基本です。
使い分けの早見
文章でまとめると、次のように判断すると迷いにくいです。
「値の大小は無視して、順番だけひっくり返したい」ならreverse()か[::-1]、「値に基づいて降順にしたい」ならsorted(..., reverse=True)またはsort(reverse=True)を選びます。
メモリに余裕がなければ就地操作、元を残したければ新しいリストを返す関数を選ぶのが実務的です。
| 方法 | 元のリスト | 返り値 | メモリ | 並べ替えの基準 | 主な用途 |
|---|---|---|---|---|---|
| list.reverse() | 変更する(就地) | None | 小 | 位置の反転のみ | 順序だけ逆にしたい、メモリ節約 |
| スライス[::-1] | 変更しない | 新しいリスト | 大(要複製) | 位置の反転のみ | 元を保持しつつ反転が必要 |
| sorted(…, reverse=True) | 変更しない | 新しいリスト | 中 | 値(安定ソート) | 降順に整列して別リストで使う |
| list.sort(reverse=True) | 変更する(就地) | None | 小 | 値(安定ソート) | 降順へ就地整列して再利用 |
- 関連記事:in演算子は遅い? リストとセット/辞書の計算量
- 関連記事:リストの要素ごとの数を速く数える
まとめ
本記事では、Pythonのリストを逆順に扱う3つの方法を、反転(list.reverse()と[::-1])と逆順ソート(sorted/sort(reverse=True))に分けて解説しました。
順序だけを逆にしたいのか、値に基づいて降順に並べたいのかをまず見極め、元のリストを残すかどうか、メモリや速度の制約に応じて選ぶと実践的です。
特にreverse()は戻り値がNone、混在型のソートはTypeErrorという落とし穴を避けつつ、目的に合う手段をシンプルに選択してください。
