そうだ! EuroPython 2011へ行こう

#2 CPythonについてのハンズオン,講演

この記事を読むのに必要な時間:およそ 12 分

Python Tips,Tricks,And Idioms:Pythonのコツ,トリック,イディオム

「Python Tips,Tricks,And Idioms」という講演では,次の項目について標準ドキュメントから要点を抜き出したPythonのチートシートを紹介するような内容でした。いくつか抜き出して紹介します。

  • Python固有の機能とイディオム
  • 比較とソート
  • イテレーターを使ったプログラミング
  • 動的なプログラミング
  • コレクション型
  • superができること
  • 最適化とハック
  • デバッガ
  • 開発ツールとその環境

本講演はPYTHON TIPS,TRICKS,AND IDIOMSから視聴できます。

key functioon

key functioon

比較とソート

次のようなソート関数(メソッド)があります。

  • sorted()
  • list.sort()
  • min()
  • max()
  • heapq.nsmallest()
  • heapq.nlargest()
  • bisect.bisect()
  • bisect.insrot()

これらはx < yの比較をするときに__lt__という特殊メソッドを呼び出します。例えば,ソートされる順序を降順にしたい場合,次のように__lt__の返り値を反転させることで実現できます。

>>> from operator import lt
>>> class MyInt(int):
...   def __init__(self, num):
...     self.num = num
...   def __lt__(self, x):
...     return not lt(self.num, x)
...
>>> sorted(map(MyInt, [1,5,3,2,4]))
[5, 4, 3, 2, 1]

またbisectモジュール以外のソート関数(メソッド)は,key引数でソートに使う値を指定することで,ソート順序をカスタマイズすることもできます。

>>> sorted("cbAdaB")
['A', 'B', 'a', 'b', 'c', 'd']
>>> # 大文字小文字を無視する
>>> sorted("cbAdaB", key=str.lower)
['A', 'a', 'b', 'B', 'c', 'd']

operatorモジュールと組み合わせても便利です。リスト内のタプルの2番目の要素でソートします。

>>> from operator import itemgetter
>>> sorted([(3, 3), (1, 2), (2, 1)], key=itemgetter(1))
[(2, 1), (1, 2), (3, 3)]

オブジェクトの属性でソートします。

>>> from operator import attrgetter
>>> from collections import namedtuple
>>> user = namedtuple('User', 'name age')
>>> sorted([user('t2y', 32), user('rokujyouhitoma', 27)],
...        key=attrgetter('age'))
[User(name='rokujyouhitoma', age=27), User(name='t2y', age=32)]

引数でソート順序を逆にできます。

>>> sorted([1, 2, 3], reverse=True)
[3, 2, 1]

ソートのサンプルについてはSorting HOW TOも参考にしてください。

イテレーターを使ったプログラミング

Pythonという言語がシンプル且つ簡潔たらしめる重要な概念がイテレーターであると説明されていました。__getitem__をサポートするシーケンスや__iter__をサポートする任意のオブジェクトが,繰り返し可能なオブジェクト(Iterable)になります(参考:第1回レポートイテレータープロトコルとジェネレーター⁠。

ファイルを10バイトずつ読み込むサンプルコードが紹介されていました。普通に実装すると次のようになります。

>>> with open(file_name)  as f:
...   while True:
...     block = f.read(10)
...     if block = '':
...       break

この処理をイテレーターを使って実装すると次のようになります。

>>> from functools import partial
>>> with open(file_name)  as f:
...   for block in iter(partial(f.read, 10), ''):
...     do_something

functools.partialは,一部の引数を渡して,関数の部分適用を行い,残りの引数を受け取る関数を生成します。一般にカリー化と呼ばれるものです。iterは,オブジェクトからイテレーターを生成します。iterの第2引数(sentinel)は,戻り値が一致したときにStopIterationを発生させる(ループを終了させる)条件として使えます。

後者の実装は,イテレーターを利用することで,ファイルの読み込みと終了条件をループ外へ切り出し,本来の目的であるblockの処理のみをループ内に記述できます。筆者はpartialiterを使ったことがなく,こういう使い方があるんだと知り,感心しました。

著者プロフィール

森本哲也(もりもとてつや)

一介のプログラマ。

自分で設計して,自分で開発して,自分で直せるような独立したプログラマを目指している。OSSコミュニティのゆるい人のつながりが性にあっていてPythonプログラミングが好き。共訳書に『エキスパート Pythonプログラミング』(アスキーメディアワークス)がある。

Twitter:@t2y

ブログ:http://d.hatena.ne.jp/t2y-1979/