Python: Pythonライクな書き方 Part1
Created at Thu, Apr 30, 2015Pythonライクな書き方について紹介します. Part1です.
for文関連
indexを使いたいとき
Bad: range, lenを使う
names = ['TDN', 'suzuki', 'tom']
for i in range(len(names)):
print i, names[i]
Good: enumerateを使う
for i, name in enumerate(names):
print i, name
2つのコレクションを扱うとき
Bad: indexを使ってアクセスする
names = ['TDN', 'suzuki', 'tom']
ages = [12, 19, 11]
for i in range(min(len(names), len(ages))):
print names[i], ages[i]
Good: 組み込み関数zipを使う
for name, age in zip(names, ages):
print name, age
dictionaryでkey, valueを使う
Bad: keyでアクセス, valueを取得する
persons = {'TDN': 12, 'tanaka': 24, 'nakata': 11}
for k in persons:
print k, persons[k]
Good: itemsメソッドを使う
persons = {'TDN': 12, 'tanaka': 24, 'nakata': 11}
for k, v in persons.items():
print k, v
値の入れかえ(swap values)
Bad: tmp変数を定義する
a = 0
b = 10
tmp = a
a = b
b = tmp
Good: onelineで交換する
a, b = 0, 10
a, b = b, a
dictionaryの作成
indexを使う方法
names = ['TDN', 'suzuki', 'tom']
d = dict(enumerate(names))
> {0: 'TDN', 1: 'suzuki', 2: 'tom'}
2つのリストのペアを使う方法
names = ['TDN', 'suzuki', 'tom']
ages = [12, 19, 11]
d = dict(zip(names, ages))
> {'tom': 11, 'suzuki': 19, 'TDN': 12}
dictionaryを使ったグルーピング
Bad: ifでいちいちチェックする
names = ['TDN', 'suzuki', 'tom', 'sato', 'seko']
d = {}
for name in names:
key = name[0]
if key not in d:
d[key] = []
d[key].append(name)
Good: setdefaultを使う
names = ['TDN', 'suzuki', 'tom', 'sato', 'seko']
d = {}
for name in names:
key = name[0]
d.setdefault(key, []).append(name)
Good: defaultdictを使う
from collections import defaultdict
names = ['TDN', 'suzuki', 'tom', 'sato', 'seko']
d = defaultdict(list)
for name in names:
key = name[0]
d[key].append(name)
効率のよいソート
Bad: cmpを用いてソートする
names = ['TDN', 'suzuki', 'tom']
def cmp_function(a, b):
if len(a) > len(b):
return 1
if len(a) < len(b):
return -1
return 0
sorted(names, cmp=cmp_function)
Good: keyを用いてソートする
sorted(names, key=len)
ファイルを開く, 閉じる時
Bad: finallyを使う
f = open('build.gradle')
try:
data = f.read()
finally:
f.close()
Good: withを使う
with open('build.gradle') as f:
data = f.read()
## リスト内包
Bad: forをわざわざ使う
```python
total = 0
for i in range(10):
total += i ** 2
Good: リスト内包, ジェネレータを使う
total = sum(i ** 2 for i in range(10))
文字列結合
Bad: stringを + でつないでいく
names = ['TDN', 'suzuki', 'tom', 'sato', 'seko']
s = ''
for name in names:
s += name
Good: joinメソッドを使う
names = ['TDN', 'suzuki', 'tom', 'sato', 'seko']
''.join(names)
デコレータを使う
Bad: メソッド内にcacheのロジックが入りこんでいる
def get_data(filepath, saved={}):
if filepath in saved:
return cache[filepath]
with open(filepaht, 'r') as f:
saved[filepath] = f.read()
return saved[filepath]
Good: cacheのロジックを外部に切り出す
from functools import wraps
def cache(func):
saved = {}
@wraps(func)
def wrapper(filepath):
if filepath in saved:
return saved[filepath]
result = func(filepath)
saved[filepath] = result
return result
return wrapper
@cache
def get_data(filepath):
with open(filepath, 'r') as f:
return f.read()
contextmanagerを使う
Before: 例外をpassしているのが正しいのか, 実装し忘れか判断が付き難い
import os
try:
os.remove('.tmp')
except OSError:
pass
After: ignoredのように明示的な名前をつけて定義する
import os
from contextlib import contextmanager
@contextmanager
def ignored(*exc):
try:
yield
except exc:
pass
with ignored(OSError):
os.remove('tmp')
まとめ
パート2に続く..