Pythonのfilterとsort
Pythonでちょっとしたツールを作る事が増えたのですが、やはりコレクション操作は重要だなと実感しました。コレクションから特定の値を抜き出したりとか、特定の値になるまでループしたりとか、やはり何するにもこの辺りはつきまとってくるんですね。
filter
と言うわけで最初はPythonでフィルター操作どうやるんだっけと言う話です。
devdocs でさっと検索してみると filter
の説明が。
https://devdocs.io/python~3.8/library/functions#filter
バッテリー同梱なんて言われている言語ですから当然基本ライブラリに含まれていますね。
第一引数にフィルター関数を、第二引数には対象となるリストをセットする感じ。
>>> def filter_function(parameter):
... parameter == 'a'
...
>>> filter(filter_function, ['a', 'b', 'c'])
第一引数の関数はラムダでも行けます。
>>> list(filter(lambda p: p == 'a', ['a', 'b', 'c']))
戻り値が iterator
なので list
関数でリスト化できます。つなげて書くとこんな感じ。
>>> list(filter(lambda p: p in filter_strings, ['a', 'b', 'c']))
リスト内包表記でも同じことを実現できます。
>>> [item for item in ['a', 'b', 'c'] if item == 'a']
['a']
>>> [item for item in range(0, 30) if item <= 15]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
sort
ソートについては以下のドキュメントに詳しく載ってました。
https://docs.python.org/ja/3/howto/sorting.html
Pythonでのソートは sorted
関数でできます。
>>> sorted([1, 3, 2, 0])
[0, 1, 2, 3]
引数にソートに使用する値を取り出す関数を与えれば複雑なリストでもソートできます。
>>> student_tuples = [
... ('john', 'A', 15),
... ('jane', 'B', 12),
... ('dave', 'B', 10)
... ]
>>> sorted(student_tuples, key=lambda student: student[2])
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
operator
モジュールにはこれらを簡略化する関数が定義されているので一般的にはこれを使うことになるのかなと。
itemgetter
はイテレーション可能な値から特定のインデックス値を指定する関数です。
attrgetter
は名前付けされた属性(Classのインスタンスなど)に対して属性を指定する関数です。
>>> from operator import itemgetter, attrgetter
>>> sorted(student_tuples, key=itemgetter(2))
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
>>> class Student:
... def __init__(self, name, grade, age):
... self.name = name
... self.grade = grade
... self.age = age
... def __repr__(self):
... return repr((self.name, self.grade, self.age))
>>> student_objects = [
... Student('john', 'A', 15),
... Student('jane', 'B', 12),
... Student('dave', 'B', 10),
... ]
>>> sorted(student_objects, key=attrgetter('age'))
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
itemgetter
と attrgetter
は複数段階でのソートを可能にしてくれます。
>>> sorted(student_tuples, key=itemgetter(1, 2))
[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]
>>> sorted(student_objects, key=attrgetter('age', 'grade'))
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
reverse
パラメータを使うと昇順と降順を指定できます。 True
の場合が降順になるようです。
>>> student_tuples = [
... ('john', 'A', 15),
... ('jane', 'B', 12),
... ('dave', 'B', 10)
... ]
>>> sorted(student_tuples, key=itemgetter(2), reverse=True)
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
Javaを使っていると compareTo
メソッドみたいな役割がないのか気になりますが、Pythonでは lt
メソッドがそれに該当します。
from operator import itemgetter, attrgetter
class Student:
def __init__(self, name, grade, age):
self.name = name
self.grade = grade
self.age = age
def __repr__(self):
return repr((self.name, self.grade, self.age))
def __lt__(self, other):
return self.age < other.age
student_objects = [
Student('john', 'A', 15),
Student('jane', 'B', 12),
Student('dave', 'B', 10),
]
sorted(student_objects)
# [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
Pythonはメソッドをクラス定義後に追加できるみたいなので、外部のライブラリに定義されている場合は以下のようにすることでソート可能なクラスにできますね。
Student.__lt__ = lambda self, other: self.age < other.age