『Pythonクックブック』のメモ

  • リストを辞書にするイディオム

過去の日記で書いたネタで、自分が発見して大発見だと思っていたら、リスト L を辞書 d にするイディオムが"4.4 シーケンスのアイテムとそのインデックスをループ処理"に載っていた。イディオムは以下の通り。インデックスが非負の有効範囲に限られるという制限と、富豪的(処理が重い)という制限があるが、手軽さの点では一番だと思う。

d=dict(enumerate(L))
  • isinstance

isinstanceはタプルを引数に取れる。

isinstance(x, (list, tuple))
  • オブジェクトの反復可能をチェックする方法

iter(obj) で TypeError を起こさなければ obj は反復可能。

  • シーケンスの複数のインデックスの値を取り出す

Pythonではmapよりリスト内包表記の方が推奨されているが、mapと特殊メソッドの組み合わせは面白い。

>>> L = ['a', 'b', 'c', 'd']
>>> idx = [3, 0, 1, 2]
>>> map(L.__getitem__, idx)
['d', 'a', 'b', 'c']
  • 辞書の値をリストにする場合の値の追加方法

有名なイディオムだと思うが、自分用のメモ。辞書 d にキー key の値のリストに value を追加。key ⇒ [value1, value2, ...] の辞書ができる。

d.setdefault(key, []).append(value)

d.setdefault(k, v) と d.get(k, v) との違いは、setdefault はキー k が辞書 d に存在しない場合、副作用として d[k] = v を行う。ちなみに、d.get(x) に近いメソッドとして、参照と削除を同時に行う d.pop(x, None) なんていうのもある。

  • 値が全て同一である辞書の作成方法
>>> dict.fromkeys('abcd', 0)
{'a': 0, 'b': 0, 'c': 0, 'd': 0}
>>> dict.fromkeys('abcd')
{'a': None, 'b': None, 'c': None, 'd': None}
>>> 
>>> import string
>>> dict.fromkeys(string.ascii_lowercase, 0)
{'a': 0, 'b': 0, ..., 'z': 0}
  • キーと値が交互に入ったリストをキーと値のタプルのリストに変換

[key1, val1, key2, val2, ...] ⇒ [(key1, val1), (key2, val2), ...] に変換。タプルのリストの形式は辞書にそのまま適用できるので、dict() や、d.update() の引数にそのまま渡せる。

def pairwise(iterable):
    itnext = iter(iterable).next
    while True:
        yield itnext(), itnext()

it = iter(iterable)、it.next() としていないところが面白いと思った。これはメソッドをバインドするかオブジェクト(データ)をバインドするかの違いで、Pythonではバインドすべきオブジェクトの境界線がないという表現が頭に残りやすいだろうか…。パフォーマンスはどうでも良いが、itnext()版はit.next()版よりも、なんと60%も早いらしい!一般的に、ドットによる属性のアクセスを避けることは重要なパフォーマンスチューニングになり得る。

  • setを使わず集合の和や交差を表現する方法

辞書のキーは重複できないことを利用して fromkeys と組み合わせる。

a = dict.fromkeys(xrange(1000))
b = dict.fromkeys(xrange(500, 1500)
union = dict(a, **b)
inter = dict.fromkeys([x for x in a if x in b])

もちろんビルトインの set を使用した方が分かりやすいし断然速いが、考え方としては面白い。

  • 辞書のキーをドットでアクセス

"4.18 名前のついたアイテム群を集めておく"に、辞書のキーをドットでアクセスする方法が書いてあった。つまり、辞書のキーをクラスや関数やモジュールの属性に変換する方法。まずはクックブックの例。

class Bunch(object):
    def __init__(self, **kwds):
        self.__dict__.update(kwds)

point = Bunch(datum=y, squared=y*y, coord=x)
print point.squared  # 参照OK
point.isok = True    # 代入もOK

以下は別解。こちらもクックブックに載っていた。これは、属性と辞書のアイテムを同一視させる方法。但しこのやり方は、keys、pop、getなどの辞書の余計なメソッドも属性として継承してしまうので、hasattr(d, attr) によってきちんとした属性チェックができないのが欠点らしい。

class DictBunch(dict):
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

モジュールを利用したやり方は西尾さんのブログを参照。

  • exec に in が指定できる

以下のように、in の後に変数を表現する辞書を指定できる。

>>> exec 'print a' in {'a': 1}
1