- Python
Python sorted・sortのkey指定|複数キー・降順混在のコピペ例
Pythonの並べ替えは、新しいリストを返す sorted() と、リスト自身を並べ替えて None を返す list.sort() の2つで、どちらも key= に「並べ替えの基準を返す関数」を渡して使います。 複数キーはタプルを返す key、降順は reverse=True、昇順と降順の混在は「数値ならマイナス」「文字列なら安定ソートを2回」で書けます。
この記事では、よく使う並べ替えパターン(辞書のリスト、dict の値、日付文字列、クラス、None を末尾へ、自然順など)をすべて Python 3.11.15 と 3.14.7 で実行し、出力をそのまま載せています。さらに、lambda・itemgetter・attrgetter の速度と、昇順降順混在の3つの書き方の速度を timeit で実測し、heapq・bisect・pandas の sort_values との使い分けも判断表にしました。
Pythonのソート早見表
まず sort と sorted の違いです。迷ったら sorted()。元のリストを書き換えてよく、メモリを節約したいときだけ list.sort() と覚えると安全です。
| 項目 | sorted(iterable) |
list.sort() |
|---|---|---|
| 元データ | 変更しない(非破壊) | 書き換える(破壊的) |
| 戻り値 | 新しいリスト | None |
| 使える対象 | リスト・タプル・文字列・dict・set・ジェネレータなど何でも | リストだけ |
| 引数 | key=、reverse= |
key=、reverse= |
| 安定ソート | はい | はい |
| メモリ | コピー1つ分多く使う | 追加のリストを作らない |
| よくある間違い | 結果を変数に受け取り忘れる | a = a.sort() で a が None になる |
やりたいこと別のコピペ表です(すべて下の各セクションに実行結果があります)。
| やりたいこと | コード |
|---|---|
| 数値を昇順 / 降順 | sorted(a) / sorted(a, reverse=True) |
| 大文字小文字を無視 | sorted(a, key=str.lower) |
| 文字列の長さ順 | sorted(a, key=len) |
| 辞書のリストをキーで | sorted(rows, key=itemgetter("age")) |
| 辞書のリストを複数キーで | sorted(rows, key=itemgetter("age", "name")) |
| dict を値で並べる | sorted(d.items(), key=itemgetter(1)) |
| dict のキーを値の順で | sorted(d, key=d.get) |
| 日付文字列(ゼロ埋めなし) | sorted(a, key=lambda s: datetime.strptime(s, "%Y/%m/%d")) |
| タプルの2番目で | sorted(a, key=itemgetter(1)) |
| クラスの属性で | sorted(objs, key=attrgetter("priority")) |
| None を末尾へ | sorted(a, key=lambda x: (x is None, x)) |
| 自然順(file2 → file10) | sorted(a, key=natural_key)(関数は後述) |
| 数値は降順、他は昇順 | sorted(a, key=lambda r: (r[0], -r[1])) |
| 文字列を降順で混在 | 後ろのキーから sorted を2回(安定ソート) |
| 上位N件だけ | heapq.nlargest(n, a, key=...) |
| 並びを保って1件追加 | bisect.insort(a, x, key=...)(3.10+) |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python sorted・sortのkey指定|複数キー・降順混在のコピペ例」
sortとsortedの違いを実行結果で確認する
list.sort() は戻り値が None です。 「並べ替えたリストを返してくれる」と思って代入すると、変数の中身が None になります。
a = [3, 1, 2]
b = sorted(a)
print(a, b)
r = a.sort()
print(a, r)
print(sorted("bca"), sorted((3, 1, 2)), sorted({"b": 1, "a": 2}))
(3, 1, 2).sort()
実行結果(Python 3.11.15 / 3.14.7 で同じ):
[3, 1, 2] [1, 2, 3]
[1, 2, 3] None
['a', 'b', 'c'] [1, 2, 3] ['a', 'b']
AttributeError: 'tuple' object has no attribute 'sort'
ポイントは次の3つです。
sorted()は元のaを変えず、新しいリストを返すa.sort()はa自体が並び替わり、戻り値はNonesorted()は何を渡しても結果は必ずリスト(文字列は1文字ずつ、dict はキーだけになる)。タプルには.sort()がないのでsorted()を使います
keyの仕組み:各要素を「比較用の値」に変換する
key= に渡す関数は、要素1つを受け取り、比較に使う値を返す関数です。Python は全要素に対して key を1回ずつ呼び、返った値どうしを比べて並べます。元の要素は変わりません。
flowchart LR
A["要素 'ccc', 'a', 'bb'"] --> B["key=len で変換<br>3, 1, 2"]
B --> C["変換後の値で比較<br>1 < 2 < 3"]
C --> D["元の要素を並べて返す<br>'a', 'bb', 'ccc'"]
key がタプルを返すと、タプルは先頭から順に比べられるので、そのまま「第1キー、同じなら第2キー」の複数キーソートになります。これがこの記事のほぼすべてのパターンの土台です。
パターン別コピペ集(すべて実行結果つき)
以下、from operator import itemgetter, attrgetter などの import を各コードに書いています。実行環境は Python 3.11.15 で、3.14.7 でも出力が同じことを確認しました。
数値を昇順・降順に並べる
print(sorted([5, 2, 9, 1]))
print(sorted([5, 2, 9, 1], reverse=True))
[1, 2, 5, 9]
[9, 5, 2, 1]
文字列を大文字小文字を無視して並べる
文字列は文字コード順なので、大文字(A〜Z)が小文字(a〜z)より前に来ます。 無視したいときは key=str.lower を使います。
words = ["banana", "Cherry", "apple", "Banana"]
print(sorted(words))
print(sorted(words, key=str.lower))
['Banana', 'Cherry', 'apple', 'banana']
['apple', 'banana', 'Banana', 'Cherry']
key=str.lower でも "banana" と "Banana" は同じキーになるので、元の順番(banana → Banana)のままです。ドイツ語の ß なども含めて揃えたいときは str.casefold が使えます。
文字列の長さ順に並べる
print(sorted(["ccc", "a", "bb", "dd"], key=len))
['a', 'bb', 'dd', 'ccc']
長さが同じ "bb" と "dd" は元の順番のままです(安定ソート。後述)。
辞書のリストを特定のキーで並べる
辞書のリストは itemgetter("キー名") が短く速い書き方です。 複数キーは itemgetter("age", "name") のように並べるだけです。
from operator import itemgetter
users = [
{"name": "佐藤", "age": 30},
{"name": "鈴木", "age": 25},
{"name": "高橋", "age": 30},
]
print(sorted(users, key=lambda u: u["age"]))
print(sorted(users, key=itemgetter("age", "name")))
[{'name': '鈴木', 'age': 25}, {'name': '佐藤', 'age': 30}, {'name': '高橋', 'age': 30}]
[{'name': '鈴木', 'age': 25}, {'name': '佐藤', 'age': 30}, {'name': '高橋', 'age': 30}]
キーがない辞書が混ざる場合は key=lambda u: u.get("age", 0) のように既定値を入れると KeyError を避けられます。
dictを値で並べる
dict 自体は sorted() に渡すとキーだけのリストになります。 値で並べるときは .items() を使い、結果を dict に戻したいなら dict() で包みます(3.7 以降の dict は挿入順を保持します)。
from operator import itemgetter
stock = {"pen": 12, "note": 5, "clip": 30}
print(sorted(stock.items(), key=lambda kv: kv[1], reverse=True))
print(dict(sorted(stock.items(), key=itemgetter(1))))
print(sorted(stock, key=stock.get))
[('clip', 30), ('pen', 12), ('note', 5)]
{'note': 5, 'pen': 12, 'clip': 30}
['note', 'pen', 'clip']
日付文字列を日付の順に並べる
2026/9/15 のようにゼロ埋めされていない日付は、文字列のままだと 2026/10/1 が先に来ます。 datetime.strptime で日付に変換する key を使います。2026-09-15 のように ISO 形式でゼロ埋めされていれば、文字列のままでも正しく並びます。
from datetime import datetime
ds = ["2026/10/1", "2026/9/15", "2025/12/31"]
print(sorted(ds))
print(sorted(ds, key=lambda s: datetime.strptime(s, "%Y/%m/%d")))
print(sorted(["2026-10-01", "2026-09-15", "2025-12-31"]))
['2025/12/31', '2026/10/1', '2026/9/15']
['2025/12/31', '2026/9/15', '2026/10/1']
['2025-12-31', '2026-09-15', '2026-10-01']
タプルのリストを並べる
タプルは何も指定しなければ「1番目 → 2番目 → …」の順で比べられます。 2番目だけで並べたいときは itemgetter(1) です。
from operator import itemgetter
pts = [(2, "b"), (1, "z"), (2, "a")]
print(sorted(pts))
print(sorted(pts, key=itemgetter(1)))
[(1, 'z'), (2, 'a'), (2, 'b')]
[(2, 'a'), (2, 'b'), (1, 'z')]
クラス(dataclass)のリストを属性で並べる
自作クラスは < が定義されていないので、key なしでは TypeError になります。 attrgetter("属性名") を渡すか、@dataclass(order=True) でフィールド順の比較を自動生成します。
from dataclasses import dataclass
from operator import attrgetter
@dataclass
class Task:
title: str
priority: int
ts = [Task("買い出し", 2), Task("請求書", 1), Task("掃除", 3)]
print(sorted(ts, key=attrgetter("priority")))
sorted(ts)
[Task(title='請求書', priority=1), Task(title='買い出し', priority=2), Task(title='掃除', priority=3)]
TypeError: '<' not supported between instances of 'Task' and 'Task'
from dataclasses import dataclass
@dataclass(order=True)
class Item:
priority: int # 先に書いたフィールドから順に比較される
title: str
print(sorted([Item(2, "b"), Item(1, "a")]))
[Item(priority=1, title='a'), Item(priority=2, title='b')]
order=True はフィールドの定義順が比較順になるので、並べたい順にフィールドを書きます。並べ方が複数あるなら、クラスに順序を持たせずに key=attrgetter(...) を都度渡すほうが読みやすくなります。
Noneを末尾(または先頭)に並べる
None と数値は比較できないので、そのままでは TypeError です。 key で (x is None, x) というタプルを返すと、False(None 以外)が True(None)より先に来るので None が末尾に集まります。
v = [3, None, 1, None, 2]
sorted(v)
TypeError: '<' not supported between instances of 'NoneType' and 'int'
v = [3, None, 1, None, 2]
print(sorted(v, key=lambda x: (x is None, x))) # None を末尾へ
print(sorted(v, key=lambda x: (x is not None, x))) # None を先頭へ
print(sorted(v, key=lambda x: (x is None, -x if x is not None else 0))) # 降順+None末尾
[1, 2, 3, None, None]
[None, None, 1, 2, 3]
[3, 2, 1, None, None]
(x is None, x) で None 同士を比べても、タプルの1番目が両方 True で2番目が None == None となり等しいと判定されるため、エラーになりません。reverse=True を使うと None も先頭へ移動するので、「降順で None は末尾」のときは3行目のように数値側をマイナスにします。
自然順(file1, file2, file10)に並べる
文字列の比較は1文字ずつなので file10 が file2 より前に来ます。 数字の部分を整数に変換する key を作ると、人が期待する自然順になります。
import re
def natural_key(s):
return [int(t) if t.isdigit() else t.lower() for t in re.split(r"(\d+)", s)]
fs = ["file10.txt", "file2.txt", "file1.txt", "File3.txt"]
print(sorted(fs))
print(sorted(fs, key=natural_key))
print(natural_key("file10.txt"))
['File3.txt', 'file1.txt', 'file10.txt', 'file2.txt']
['file1.txt', 'file2.txt', 'File3.txt', 'file10.txt']
['file', 10, '.txt']
re.split(r"(\d+)", ...) は数字部分も結果に残すので、["file", 10, ".txt"] のような「文字列と整数が交互に並ぶリスト」になり、同じ位置どうしで比べられます。バージョン番号(1.10.0 と 1.9.2)を厳密に扱うなら、PyPI の packaging.version.Version を key に使う方法もあります。
日本語の文字列を並べる
Python の標準の並べ替えは Unicode のコードポイント順です。 ひらがな → カタカナ → 漢字の順に並び、漢字は読み仮名ではなく文字コード順になります。
print(sorted(["りんご", "バナナ", "みかん", "あんず", "イチゴ", "柿", "梨"]))
['あんず', 'みかん', 'りんご', 'イチゴ', 'バナナ', '柿', '梨']
漢字を読み仮名の五十音順で並べる機能は標準ライブラリにはありません。 読み仮名が必要な場合は、データ側に「よみ」列を持たせて key=itemgetter("yomi") で並べるのが確実です。この記事では読み仮名の自動変換は扱いません。
昇順と降順を混在させる3つの方法
「部署は昇順、年齢は降順」のように、キーごとに向きが違う並べ替えです。reverse=True は全キーに効くので、ここが一番つまずきやすいところです。次のデータで比べます。
members = [
("ops", 30, "Sato"),
("dev", 25, "Suzuki"),
("dev", 35, "Takahashi"),
("ops", 30, "Ito"),
("dev", 25, "Abe"),
]
方法1:数値のキーはマイナスにする
降順にしたいキーが数値なら、- を付けるだけです。 一番短く読みやすい書き方です。
print(sorted(members, key=lambda m: (m[0], -m[1])))
[('dev', 35, 'Takahashi'), ('dev', 25, 'Suzuki'), ('dev', 25, 'Abe'), ('ops', 30, 'Sato'), ('ops', 30, 'Ito')]
文字列には - が使えません。sorted(["b", "a"], key=lambda s: -s) は TypeError: bad operand type for unary -: 'str' になります。
方法2:安定ソートを使って後ろのキーから2回並べる
Python のソートは安定ソート(同じキーの要素は元の順番を保つ)です。 これを利用して、優先度の低いキーから順に並べ替えると、文字列の降順も混ぜられます。
from operator import itemgetter
# 部署は昇順、名前は降順
s = sorted(members, key=itemgetter(2), reverse=True) # 2番目の優先度(名前・降順)
s = sorted(s, key=itemgetter(0)) # 1番目の優先度(部署・昇順)
print(s)
# 3キー:部署昇順 → 年齢降順 → 名前昇順
s = sorted(members, key=itemgetter(2))
s = sorted(s, key=itemgetter(1), reverse=True)
s = sorted(s, key=itemgetter(0))
print(s)
[('dev', 35, 'Takahashi'), ('dev', 25, 'Suzuki'), ('dev', 25, 'Abe'), ('ops', 30, 'Sato'), ('ops', 30, 'Ito')]
[('dev', 35, 'Takahashi'), ('dev', 25, 'Abe'), ('dev', 25, 'Suzuki'), ('ops', 30, 'Ito'), ('ops', 30, 'Sato')]
順番を逆にすると、最後に並べたキーが優先されます。 部署を先に並べてから名前で並べると、部署がばらばらになります。
s = sorted(members, key=itemgetter(0))
s = sorted(s, key=itemgetter(2), reverse=True)
print(s)
[('dev', 35, 'Takahashi'), ('dev', 25, 'Suzuki'), ('ops', 30, 'Sato'), ('ops', 30, 'Ito'), ('dev', 25, 'Abe')]
reverse=True を付けても安定性は保たれます(同じキーの要素は元の順番のまま)。
方法3:cmp_to_keyで比較関数を書く
「x が先なら負、同じなら 0、y が先なら正」を返す比較関数を functools.cmp_to_key で key に変換する方法です。条件が複雑なときに読みやすくなります。
from functools import cmp_to_key
def compare(x, y):
if x[0] != y[0]: # 部署:昇順
return -1 if x[0] < y[0] else 1
if x[2] != y[2]: # 名前:降順
return -1 if x[2] > y[2] else 1
return 0
print(sorted(members, key=cmp_to_key(compare)))
[('dev', 35, 'Takahashi'), ('dev', 25, 'Suzuki'), ('dev', 25, 'Abe'), ('ops', 30, 'Sato'), ('ops', 30, 'Ito')]
3つの方法の速度と選び方
10万件のタプル(部署5種・年齢・ID)で「部署昇順・年齢降順」を3つの方法で並べ、timeit で計測しました(Python 3.11.15、5回中の最小値、結果は3つとも一致を確認)。
| 方法 | 1回あたり | 向いている場面 |
|---|---|---|
タプル+マイナス key=lambda e: (e[0], -e[1]) |
93.4 ms | 降順キーが数値。1行で書きたい |
| 安定ソート2回 | 54.5 ms | 文字列の降順を混ぜたい。件数が多い |
cmp_to_key |
320.5 ms | 条件が複雑で比較関数のほうが読みやすい |
安定ソート2回のほうがタプルの key より速いという結果でした。タプルを10万個作って要素ごとに比べるより、単純な値で2回並べるほうが軽いためです。Python のソート(Timsort/3.11 以降は Powersort 系のマージ規則)は、すでに並んでいる部分を活かせるので、2回目の並べ替えも速く終わります。cmp_to_key は比較のたびに Python の関数を呼ぶため約3〜6倍遅いので、件数が多いときは方法1か2を選ぶとよいです。
lambda・itemgetter・attrgetterの速度を実測
key の書き方による速度差を、10万件で計測しました(20回実行×5セットの最小値。単位 ms)。
| 対象 | lambda | itemgetter / attrgetter | 差 |
|---|---|---|---|
タプル r[0](3.11.15) |
20.29 | 17.83 | 約12%速い |
辞書 d["score"](3.11.15) |
20.66 | 19.67 | 約5%速い |
属性 o.score(3.11.15) |
20.07 | 18.94 | 約6%速い |
| 2キー(辞書)(3.11.15) | 47.54 | 47.34 | ほぼ同じ |
タプル r[0](3.14.7) |
23.18 | 20.67 | 約11%速い |
属性 o.score(3.14.7) |
22.47 | 22.44 | ほぼ同じ |
| 2キー(辞書)(3.14.7) | 56.37 | 59.89 | lambda がやや速い |
計測コード(そのまま実行できます):
import timeit, random
from operator import itemgetter, attrgetter
from dataclasses import dataclass
random.seed(0)
N = 100_000
rows = [(random.randint(0, 999), random.random()) for _ in range(N)]
dicts = [{"id": i, "score": r[0]} for i, r in enumerate(rows)]
@dataclass
class P:
id: int
score: int
objs = [P(i, r[0]) for i, r in enumerate(rows)]
def t(stmt):
n = 20
return min(timeit.repeat(stmt, globals=globals(), number=n, repeat=5)) / n * 1000
for stmt in [
"sorted(rows, key=lambda r: r[0])",
"sorted(rows, key=itemgetter(0))",
"sorted(objs, key=lambda o: o.score)",
"sorted(objs, key=attrgetter('score'))",
]:
print(f"{stmt}: {t(stmt):.2f} ms")
結論:差は最大でも1割程度で、2キー以上ではほぼ同じです。 速度より読みやすさで選んでかまいません。目安としては次のとおりです。
| 書き方 | 選ぶとよい場面 |
|---|---|
itemgetter("a", "b") |
辞書・タプルのキー指定をそのまま書くとき(短く、意図が明確) |
attrgetter("a") |
オブジェクトの属性。"user.name" のようなドット区切りも可 |
lambda |
変換・計算が入るとき(-x、len、strptime、is None など) |
なお、key なしでタプル全体を比較した sorted(rows) は 44.01 ms で、key=itemgetter(0) の約2.5倍かかりました。必要なキーだけを key で取り出すと速くなります。
安定ソートとは:同じキーは元の順番を保つ
Python の sorted() と list.sort() は安定ソートであることが公式に保証されています。 同じキーの要素は、並べ替え前の順番を保ちます。
from operator import itemgetter
data = [("b", 1), ("a", 1), ("c", 0)]
print(sorted(data, key=itemgetter(1)))
[('c', 0), ('b', 1), ('a', 1)]
("b", 1) と ("a", 1) はキーが同じなので、元の順番(b → a)のままです。前章の「安定ソート2回」はこの性質を使っています。
よくあるエラーと直し方
| エラー・症状 | 原因 | 直し方 |
|---|---|---|
変数が None になる |
a = a.sort() と代入した |
a.sort() だけ書く、または a = sorted(a) |
'<' not supported between instances of 'str' and 'int' |
数値と文字列が混在 | key=int などで型を揃える |
'<' not supported between instances of 'NoneType' and 'int' |
None が混在 | key=lambda x: (x is None, x) |
'<' not supported between instances of 'Task' and 'Task' |
自作クラスに比較がない | key=attrgetter(...) か @dataclass(order=True) |
bad operand type for unary -: 'str' |
文字列にマイナスを付けた | 安定ソート2回か cmp_to_key |
'tuple' object has no attribute 'sort' |
タプルに .sort() |
sorted(t) を使う |
KeyError |
辞書にキーがない要素がある | key=lambda d: d.get("k", 既定値) |
file10 が file2 より前 |
文字列の比較 | 自然順の natural_key |
数値と文字列が混ざったデータは、key=int で比較用の値だけ揃えると、元の型を保ったまま並べられます。
print(sorted([1, "2", 3], key=int))
[1, '2', 3]
heapq・bisect・pandasとの使い分け
全件を並べる必要がないときは、sorted() 以外の道具のほうが速く・簡単になることがあります。
| やりたいこと | 道具 | 理由 |
|---|---|---|
| 全件を並べたい | sorted() / list.sort() |
基本はこれ |
| 最大・最小の1件だけ | max() / min()(key= 可) |
並べ替え不要 |
| 上位・下位のN件だけ | heapq.nlargest() / nsmallest() |
全件を並べずに済む |
| 並んだリストに1件ずつ追加 | bisect.insort() |
毎回 sort() し直さなくてよい |
| 並んだリストで位置を探す | bisect.bisect_left() |
二分探索で速い |
| 表形式データ・欠損値あり | pandas sort_values() |
列ごとの昇順降順、NaN の位置を引数で指定 |
heapq.nlargestで上位N件だけ取り出す
import heapq
from operator import itemgetter
items = [
{"name": "pen", "stock": 12},
{"name": "note", "stock": 5},
{"name": "clip", "stock": 30},
{"name": "tape", "stock": 8},
]
print(heapq.nlargest(2, items, key=itemgetter("stock")))
print(heapq.nsmallest(2, items, key=itemgetter("stock")))
[{'name': 'clip', 'stock': 30}, {'name': 'pen', 'stock': 12}]
[{'name': 'note', 'stock': 5}, {'name': 'tape', 'stock': 8}]
100万件の整数から上位10件を取る時間を比べました(Python 3.11.15、3回中の最小値)。
| 方法 | 時間 |
|---|---|
sorted(scores, reverse=True)[:10] |
392.9 ms |
heapq.nlargest(10, scores) |
28.2 ms |
max(scores)(1件だけ) |
9.6 ms |
上位N件だけなら heapq.nlargest が約14倍速い結果でした。N が全体の大半になるなら sorted() のほうが向いています。
bisectで並びを保ったまま追加する
import bisect
from operator import itemgetter
a = [10, 20, 30]
bisect.insort(a, 25)
print(a)
tasks = [(1, "請求書"), (3, "掃除")]
bisect.insort(tasks, (2, "買い出し"), key=itemgetter(0)) # key= は Python 3.10 以降
print(tasks)
print(bisect.bisect_left([10, 20, 30], 20), bisect.bisect_right([10, 20, 30], 20))
[10, 20, 25, 30]
[(1, '請求書'), (2, '買い出し'), (3, '掃除')]
1 2
bisect の key= 引数は Python 3.10 で追加されました。リストはあらかじめ同じ基準で並んでいる必要があります。
pandasのsort_valuesで列ごとに昇順降順を指定する
pandas では ascending=[True, False] のようにキーごとの向きをリストで渡せます。 欠損値(NaN)は既定で末尾、na_position="first" で先頭です(pandas 3.0.2 で確認)。
import pandas as pd
df = pd.DataFrame({
"team": ["dev", "ops", "dev", "ops"],
"age": [25, 30, 35, None],
"name": ["Suzuki", "Sato", "Takahashi", "Ito"],
})
print(df.sort_values(["team", "age"], ascending=[True, False]))
print(df.sort_values("age", na_position="first"))
print(df.sort_values("name", key=lambda s: s.str.lower()).name.tolist())
team age name
2 dev 35.0 Takahashi
0 dev 25.0 Suzuki
1 ops 30.0 Sato
3 ops NaN Ito
team age name
3 ops NaN Ito
0 dev 25.0 Suzuki
1 ops 30.0 Sato
2 dev 35.0 Takahashi
['Ito', 'Sato', 'Suzuki', 'Takahashi']
pandas の key= は列(Series)全体を受け取る関数である点が、組み込みの sorted()(要素1つを受け取る)と違います。
よくある質問
Q. sortとsorted、どちらを使えばよいですか?
A. 基本は sorted() がおすすめです。 元のデータを残せて、リスト以外(タプル・dict・ジェネレータ)にも使えます。大きなリストを並べ替えて元の順番が不要なときは、コピーを作らない list.sort() がメモリの節約になります。
Q. 複数キーで一部だけ降順にするには?
A. 降順のキーが数値なら key=lambda r: (r[0], -r[1]) のようにマイナスを付けます。 文字列の降順を混ぜるときは、優先度の低いキーから sorted() を2回以上かける方法(安定ソート)が簡単で、計測でも速い結果でした。
Q. reverse=Trueと key=lambda x: -x は同じですか?
A. 数値なら結果は同じです。どちらも安定ソートなので、同じ値の要素は元の順番を保ちます(reverse=True は「逆順に並べ直す」のではなく「降順で比較する」動きです)。文字列や None にはマイナスが使えないので、単純な降順なら reverse=True を使うのが分かりやすいです。
Q. 日本語を五十音順(読み順)で並べられますか?
A. 標準の並べ替えは Unicode 順なので、ひらがな・カタカナはおおむね五十音順になりますが、漢字は読み順になりません。 読み仮名の列をデータに持たせて、その列を key にするのが確実です。
まとめ
sorted()は新しいリストを返す(非破壊)、list.sort()はリスト自身を並べ替えてNoneを返す(破壊的)key=は「要素を比較用の値に変換する関数」。タプルを返せば複数キーソートになる- 辞書は
itemgetter、属性はattrgetter、計算が入るならlambda。速度差は最大1割程度なので読みやすさで選んでよい - None は
(x is None, x)、自然順は数字をintにするkeyで並べられる - 昇順と降順の混在は、数値ならマイナス、文字列なら安定ソートを後ろのキーから2回。10万件では2回ソートが最速、
cmp_to_keyは約3倍以上遅い - 上位N件は
heapq.nlargest、並びを保った追加はbisect.insort、表データと欠損値は pandassort_valuesと使い分ける
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python sorted・sortのkey指定|複数キー・降順混在のコピペ例」
株式会社RJCでは、Pythonの並べ替えのような基本の道具を、実行結果で確かめながら仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。