正しい解決策はkey
、unutbu の回答に示されているように、タプルを返す関数を使用することです。ただし、別の方法があります。Python のソートは安定していることが保証されているため、異なるキーで複数のソートを実行して、必要な出力を得ることができます。特に:
list_.sort(key=lambda x: float(x[1]))
list_.sort(key=lambda x: len(x[1]))
list_.sort(key=lambda x: x[0])
IPython を使用したデモ:
In [1]: list_ = [(1, '0101'), (1, '1010'), (1, '101'), (2, '01'), (2, '010'), (2, '10')]
In [2]: list_.sort(key=lambda x: float(x[1]))
...: list_.sort(key=lambda x: len(x[1]))
...: list_.sort(key=lambda x: x[0])
...:
In [3]: list_
Out[3]: [(1, '101'), (1, '0101'), (1, '1010'), (2, '01'), (2, '10'), (2, '010')]
注: この解決策は、質問で説明した 3 つの手順に似ていますが、手順が逆になっています。正しい出力を得るには、主キーの最後で並べ替えます。
また、ソートに使用されるアルゴリズムは適応型であることにも注意してください。これは、シーケンスがすでに部分的にソートされている場合、部分的な順序を使用してより効率的にソートできることを意味します (多くの場合、ではなく線形時間でnlog(n)
)。複数のキーで並べ替えると、多くの場合、この部分的な順序が達成されるため、複数回呼び出してsort()
もそれほどコストはかかりません。ただし、キーとデータに大きく依存します。タプルをキーとして使用するよりも効率的な場合もあれば、非常に遅い場合もあります。
タイミングの一例。2 つのソリューションには、ほとんど同じ時間がかかることに注意してください。
In [9]: list_
Out[9]: [(1, '0101'), (1, '1010'), (1, '101'), (2, '01'), (2, '010'), (2, '10')]
In [10]: list_ *= 1000 # better to avoid too small benchmarks.
In [11]: %%timeit
...: a = sorted(list_, key=lambda x: (x[0], len(x[1]), float(x[1])))
...:
100 loops, best of 3: 6.04 ms per loop
In [12]: %%timeit
...: a = sorted(list_, key=lambda x: float(x[1]))
...: a.sort(key=lambda x: len(x[1]))
...: a.sort(key=lambda x: x[0])
...:
100 loops, best of 3: 5.72 ms per loop
In [13]: import random
...: data = [(random.randint(1, 1000), bin(random.randint(1, 100))[2:]) for _ in range(10000)]
...:
In [14]: %%timeit
...: a = sorted(data, key=lambda x: (x[0], len(x[1]), float(x[1])))
...:
100 loops, best of 3: 15.2 ms per loop
In [15]: %%timeit
...: a = sorted(data, key=lambda x: float(x[1]))
...: a.sort(key=lambda x: len(x[1]))
...: a.sort(key=lambda x: x[0])
...:
100 loops, best of 3: 15.1 ms per loop