読者です 読者をやめる 読者になる 読者になる

タオルケット体操

サツバツいんたーねっと

覚えるだけでPythonのコードが少し綺麗になる頻出イディオム

プログラミング python

まえがき

今年の春から今まで、2年ぶりにPythonを沢山書いているわけなんですが、JavaScriptのクソに頭をやられて久しぶり書くだけあって基本的なところから色々と頭から抜け落ちていたわけです。
そんで何か思い出すたびに会社のwikiを使ってメモっていたのですが、せっかくなので少々訂正をしてブログにも書きます。

知ってる人は当然知ってる、でも結構長いことPythonを書いてても知らなかったりするような小技を載っけました。
なお、メタプログラミングとかの黒魔術っぽい記事のまとめはこちら:

hachibeechan.hateblo.jp

リストの処理

インデックスを取りながら値も欲しい

Not Good:

datas = ['a', 'b', 'c']
for i in range(len(data)):
    print(i)
    print(datas[i])

Good:

datas = ['a', 'b', 'c']
for i, data in enumerate(datas):
    print(i)
    print(data)

複数のリストを同時に処理したい

Not Good:

data_a = ['a', 'a', 'a', 'a']
data_b = ['b', 'b', 'b', 'b']
min_length = min(len(data_a), len(data_b))
for i in range(min_length):
    a = data_a[i]
    b = data_b[i]

Good:

for a, b in zip(data_a, data_b):
    # use a, b

NOTE:

Generatorの概念が理解できているならばitertools.izipを使いましょう。ですが、貴方がバージョン3以上のPythonを使っているならば、デフォルトでzipはGeneratorを返します。
他にも、itertoolには数多くの便利な関数が用意されているので一通り目を通しておきましょう。扱いたいリストたちの状態次第では、izip_longest(あるいはzip_longest)が役に立つでしょう。

要素をリストに追加していきたい

Not Pythoniac:

li = []
for i in range(10):
    li.append(i)

Pythoniac:

li = [i for i in range(10)]

条件に合った要素だけをリストに追加していきたい

same as filter()

Not Pythoniac:

li = []
for i in range(10):
    if i % 2 == 0:
        continue
    li.append(i)

Pythoniac:

li = [i for i in range(10) if not i % 2 == 0]

リストの要素それぞれに変化を加えていきたい

same as map()

Not Pythoniac:

li = []
for i in range(10):
    li.append(i * 2)

Pythoniac:

li = [i * 2 for i in range(10)]

You can use this with the filter() pattern:

li = [i * 2 for i in range(10) if i % 2 == 0]

リスト内包表記以外のシンタックスについて

Generator

(i * 2 for i in range(10) if i % 2 == 0)

Set(集合型)

{i * 2 for i in range(10) if i % 2 == 0}

Dictionary

{i: i * 2 for i in range(10) if i % 2 == 0}

辞書の処理

keyが存在するかどうかをチェックする

Bad:

some_dictionary = some_func()
try:
    some_dictionary['foo']
except KeyError:
    print('no key!')

Not Efficient:

if some_dictionary.get('foo') == None
    print('no key!')

Good:

if 'foo' in some_dictionary:
    print('no key!')

NOTE:

has_key新しいPythonでは使えません。

デフォルトの値を持った辞書を作成したい

Not Efficient:

result = {}
for key, v in some_values:
    if key not in result:
        result[key] = []
    result[key].append(v)

Efficient:

from collections import defaultdict

result = defaultdict(list)
for key, v in some_values:
    result[key].append(v)

値が存在しない場合にだけ要素を追加したい

Not Efficient:

some_dictionary = some_func()
if 'foo' in some_dictionary:
    some_dictionary['foo'] = 'default value'

Efficient:

some_dictionary.setdefault('foo', 'default value')

要素の数え上げをしていきたい

Not Efficient:

data = ['aaa', 'bbb', 'ccc', 'aaa', 'ddd']

word_and_counts = {}
for word in data:
    if word_and_counts.has_key(word):
        word_and_counts[word] += 1
    else:
        word_and_counts[word] = 1

Efficient:

from collections import Counter

data = ['aaa', 'bbb', 'ccc', 'aaa', 'ddd']
counter = Counter(data)

その中でも最も頻度の高いものを取得する

If dict:

top_n =[]
count = 0
for w, c in sorted(word_and_counts.iteritems(), key=lambda x: x[1], reverse=True):
    if count == 3:
        continue
    result[w] = c
    count = count + 1

If Counter:

top_n = counter.most_common(3)

ValueObjectを作りたい

Not Efficient:

and this is not Immutable

class Rect(object):
    x = 0
    y = 0
    width = 0
    height = 0

    def __init__(x, y, width, height):
        # ellipsis

rect = Rect(0, 0, 0, 0)

Efficient:

and this is immutable

from collections import namedtuple

Rect = namedtuple('Rect', ('x', 'y', 'width', 'height', ))
rect = Rect(0, 0, 0, 0)

所感

こうして振り返るとリスト処理関係のイディオムが多い気がしますね。
何か気がつくことや、要望があれば随時書き足していきます。


Effective Python ―Pythonプログラムを改良する59項目

Effective Python ―Pythonプログラムを改良する59項目

サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考

サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考

  • 作者: Justin Seitz,青木一史,新井悠,一瀬小夜,岩村誠,川古谷裕平,星澤裕二
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2015/10/24
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログ (10件) を見る