Pythonを始めた時から知っていたかったベターな書き方
かなり基本的な内容です.それと記事に一貫性があるわけではないです.
1. with statement
定番のwith
です.ほとんどの場合で使えると思うので,積極的に使いましょう.
Go
では同じようなことをdefer
を使ったりしますね.
# not good... f = open("./test.txt") foo = f.read()とかその辺 f.close()
# better! with open("./test.txt") as f: foo = f.read()
上のコードでは,例えばfoo = f.read()
の部分のコードが長い場合,f.close()
を忘れがちですよね.
下のコードではそのような心配が入りませんし,簡潔です.
2. collections
python
はよくbattery included
な言語と言われますが,その名の通り外部のライブラリに
あまり頼らず標準ライブラリだけで結構戦えます.
collections
も便利な標準ライブラリの一つです.collections
の中でも特にCounter
やdefaultdict
は
使う機会が個人的に多いです.
どちらもdict
型によく似た構造体ですが,dict
型にはない便利な機能が付いています.
- Counter
Counter
はその名の通り引数として渡されたiterable
なものの要素の数を数えてくれます.できたものはkey
に要素,value
に要素の数が入ります. またそれ以外にも,most_common(n)
というメソッドを使うと最頻出の要素から上位n
個の要素を要素をとって来てくれます.
from collections import Counter c = Counter([1,1,2,3,3,4]) print(c.most_common(2)) # ==> [(1, 2), (3, 2)]
同じことはdict
型にもできますが,よくやりがちなミスが下のような感じです.
d = {} for token in [1,1,2,3,3,4]: d[token] += 1 # ==> 当然エラー.初期値が割り当てられていない,
setdefault
などで頑張ってもいいですが,あまり美しくないですよね,
- defaultdict
defaultdict
もdict
型に似たようなクラスです.先ほどのダメなコードの例では初期値が割り当てられていなくて エラーとなっていましたが,dict
を使っていて最初から初期値を割り当てておいて欲しいということはよくあります. そんなときにこういう風に書くと便利です.
from collections import defaultdict d = defaultdict(list) for i, token in enumerate([1,1,2,3,3,4]): d[token].append(i) print(d) # ==> defaultdict(<class 'list'>, {1: [0, 1], 2: [2], 3: [3, 4], 4: [5]})
このように,定義するところで最初に空のlist
を初期値にすると示すだけで,新たなkey
でアクセスされた時に
空のlist
を最初から割り当てていてくれます.
3. sort
あるlist
やdict
をソートした上でイテレーションを回したいことはよくあると思います.list
はともかく,
dict
の場合はkey
でソートしたいときやvalue
でソートしたいとき,色々な場合があると思います.
d = {2: 3, 1: 4, 5: 1} for k, v in sorted(d.items()): print(k, v) # ==> 1 4, 2 3, 5 1の順番.keyで昇順の並び for k, v in sorted(d.items(), key=lambda x:x[1]): print(k, v) # ==> 5 1, 2 3, 1 4の順番.valueで昇順の並び
もっと複雑な場合,例えばPythonの公式ドキュメントでは
operator
モジュールを利用して以下のようなコードを紹介しています.
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)) student_objects = [ Student('john', 'A', 15), Student('jane', 'B', 12), Student('dave', 'B', 10), ] print(sorted(student_tuples, key=itemgetter(2))) # ==> [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] print(sorted(student_objects, key=attrgetter('age'))) # ==> [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] # gradeでソートしてさらにageでソートする場合 print(sorted(student_tuples, key=itemgetter(1,2))) # ==> [('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)] print(sorted(student_objects, key=attrgetter('grade', 'age'))) # ==> 上と一緒
4. all(), any()
以前に少しだけ書いたことがありますが,any
とall
はかなり便利です.
例えば,以下のようなコードを書いている場合,allを使うとスッキリかけます.(ちょっと例えが極端ですが...)
# not good... cnt = 0 l = [1,1,2,3,4,4,5] for i in l: if i < 10: cnt += 1 if cnt == len(l): print("All numbers are less than 10")
# better! if all(x < 10 for x in l): print("All numbers are less than 10") if any(x >= 10 for x in l): print("All numbers are less than 10")
5. ファイルオブジェクトに対するイテレーション
Python
では,ファイルオブジェクトに対してイテレーションをかけられます.巨大なファイルを行ごとに読みたい
なんて時にすごく便利です.
with open("./test.txt") as f: for line in f: print(line)
公式ドキュメントのとおり細かい調整(シーケンス場所を変えるなど)はできませんが,多くの場合はこの書き方だけ覚えておけば十分でしょう.
5. sum
sum
関数はジェネレータも取ることができます.例えば以下のようなコードだと,わざわざlist
を生成するより早いです.
In [1]: %timeit sum(x for x in range(100)) 100000 loops, best of 3: 6.21 µs per loop In [2]: %timeit sum([x for x in range(100)]) 100000 loops, best of 3: 6.94 µs per loop
sum
以外にも結構こういう風に書けるのは多いので,遅いなーと思ったらジェネレータで書いてみるのもいいでしょう.
以上です.もっと紹介したいのですがめんどくなったので終わります. もし何か間違いやもっといい書き方などあれば教えてくれると嬉しいです.