1 章テキスト の中で、抜群に重要なのはこれだと思う。ある種の thon プログラムが遅すぎる原因として最も多いのが、 大きな文字列を + や + = で構築していることだからだ。これを絶対にやらないよう自分を訓練せよ。このレシ ピで推奨した ' . join アプローチを使うのだ。 こうした + = の誤用によるパフォーマンス低下の改善を試みた。 . j0in の方が依 thon2.4 では勇敢にも、 然はるかに高速で好ましいとはいえ、一部の初心者および不注意なプログラマが捨てるマシンサイクルは多 少減少した。同様に psyco (Python の just-in-time リ IT ] 特化コンパイラで http://psyco.sourceforge.net/ にあ る ) では + = ペナルティがさらに減る。しかしながら、どの場合でも依然 ' ・ . j0in がベストアプローチなのである。 「シーケンス」とは ト、 operator モジュールの各項。 ライプラリリファレンスおよび fPython クイックリファレンス』の文字列メソッド、文字列のフォー 参照 に、用語的厳密性を逸脱することにしたのである。 妙に不正確だが文脈から明らかな用語を使った方が読みやすくなり、さまざまな「声」を保存できるという場合 問題は、本が実用的なプロク、ラミング書ではなく数学の教科書に見えてしまうことだ。そんなわけで我々は、微 of st 「 ing) 」であればどんなものでもよい。すべての場所で「結合された反復可能体」という用語を使うことの な文字列によるシーケンス」と言っているが、これは実際には「結合された文字列反復可能体 ( bounded ite 「 able (bounded ite 「 able) 」と書くべきだったところがたくさんある。たとえは先のレシピの「解法」の冒頭で「小さ ス、反復可能体、さらにはリストと書いてある場所にさえ、厳密な用語を使うなら常に「結合された反復可能体 いての参考となる。クックプックの編者たちはこの用語集にある通りの語法に沿うよう努めはしたが、シーケン http://www.python.0 「 g/moin/PYthonGlossa 「 Y には PYthon 用語集があり、上記を含むさまざまな用語につ sum 、 st て . j0in 他多くのビルトイン ) で使える。 れと同等の多くのコンテキスト ( リスト内包の for 節や Py 物 on 2.4 のジェネレータ式、さらには min 、 max 、 zip 、 他多数があり、反復子 ( イテレータ ) とジェネレータもこれに含まれる。すべての反復可能体はループ文およびこ リのキーを任意の順序で与える ) 、ファイルオブジェクト ( 反復はテキストファイルの行を 1 行すっ与える ) 、その 能体 bounded ite 「 a 団 e というべきだ ) 。シーケンスでない反復可能体にはディクショナリ ( 反復はディクショナ だ。このときは反復可能体 ( ite 「 a e ) というべきである ( 有限要素であることに着目するなら、結合された反復可 インデクシング、スライシンク、、 len を必要としないことは多々ある一一 - アイテム 1 つ 1 つの反復で十分な場合 クト、タブル、 array. arrays など ) 。 となる。もっともよく見る「シーケンス」は list であるが、他にも多くの種類がある ( 文字列、 unicode オブジェ であり、インデクシング、スライシング、および組み込み関数 len ( コンテナ中のアイテム数を返す ) に渡せるもの つだ。シーケンスを厳密に言うと有限数の要素 ( アイテム ) を 1 つづつ反復的に取り出すことのできるコンテナ Python には sequence 型というものはないが、それでも「シーケンス」は PYthon でもよく使われる用語の 1
4.4 シーケンスのアイテムとそのインデックスをループ処理ー 159 4.4 シーケンスのアイテムとそのインデックスをループ処理 Credit: AIex Martelli, Sami Hangaslammi 訳 : 鴨澤眞夫 問題 シーケンスにループをかけたい。各ステップでシーケンスのどこまで来たかを見るため、イン デックス値も知る必要がある ( たとえばシーケンス中のエントリの一部をリバインドしたい ) が、 python で好まれるループ手法はインデックスを使わない。 ビルトイン関数 enumerate の出番はこ 解法 が、そちらよりも上記の方がクリーンで読みやすく高速である。ちなみに代案では以下のようにする : 代案としてはインデックスでループして、アイテムにもインデクシングでアクセスする手法が考えられる sequence[index] = transform(item) if item > 23 : for index, item in enumerate(sequence) : 推奨している。言い換えれば、シーケンスの各アイテムを得るパイソニックな手法は : シーケンスのループはやたらに必要なものだが、 Python ではシーケンスに直接ループを掛けることを強く 考察 sequence[index] = transform(sequence[index]) if sequence[index] > 23 : for index in range(len(sequence)) : for item in sequence: このインデックスを使って対応のアイテムを取得する : を使うことである。低水準言語で典型的な間接アプローチ、すなわちシーケンスのインデックスにループを process(item) かけ、 process(sequence[index]) for index in range(len(sequence)) : 代入する必要がある。 Python がビルトイン関数 enume て ate を提供しているのはこのためだ。 enumerate はあら のがリスト中のエントリにリバインドを行う場合で、このときには新しいアイテムを thelist [ index] に対して とはいえ、ループ中でインデックスやその対応アイテムを知る必要が生することはある。非常によくある りループ可能であるが、インデクシングが有効なのはリストのようなシーケンスのみ ) 。 ではない。直接ループはクリーンで読みやすく高速、かつ一般性が高い ( すべての反復可能型はその定義によ
162 ー 4 章ショートカット 4.6 入れ子になったシーケンスの平滑化 Credit: Luther BIissett, HoIger ・ ekel, Hemanth Sethuram, ParzAspen Aspen 訳 : 鴨澤眞夫 問題 シーケンス中のアイテムはシーケンスかもしれない。そのサプシーケンスのアイテムも同様であ り、結局任意の深さの“入れ子”となっているとする。このシーケンスを「平らに」延ばして ループをかけたい。つまり、サプシーケンスのすべてを“展開”して、スカラーアイテム ( スカ ラーまたはアトムとはシーケンスでないもの。入れ子シーケンスを木に見立てると葉にあたるも の ) から成るフラットなシーケンスとしたい。 解法 扱っている要素のうち、いすれが、・展開 " すべき " サプシーケンス " であり、いすれがあるがまま引き渡 すべき " スカラー " であるのか、伝達できる必要がある。こでは一般性を考慮し、「どの」アイテムを展開 すべきかを判断する「述語」を引数に取れるようにする ( 述語とは、あらゆる要素に対してコール可能で、真 偽値を返す関数である。この場合は、要素が展開すべきサプシーケンスであれば真、そうでなければ偽 ) 。と りあえすのデフォルトとして、リストとタブルのみをすべて、・展開 " するとしよう。このとき、再帰ジェネ レータがもっともシンプルな解となる : def list or tuple(x) : return isinstance(), (list, tuple)) def flatten(sequence, to expand=list or tuple) : fO て item in sequence: if to expand(item): for subitem in flatten(item, t0 expand) : yield subitem else: yield item 考察 入れ子シーケンスの平滑化、等価な言い方で " 木 " のすべての葉を連続的に " 歩む " ことは、多様なアプ リケーションで普通に見られるタスクだ。アイテムをシーケンスやサプシーケンスの形でグループ化してあ る入れ子構造を使っているが、構造を考慮しない用途がある場合だ。要するに x in flatten([l, 2 , [ 3 , [ ] , 4 , [ 5 , 6 ] , 7 , [ 8 , ] , いだけなのである。たとえば : このごく普通のタスクに生する唯一の問題として、一般的には何が " 展開 " で 1 2 3 4 5 6 7 8 9 を出力したいのだ。 print x, アイテムを 1 つ 1 つ扱いた され、何がスカラーとしてそ
5.8 シーケンスのアイテムを小さい方から取る一 211 このコード片の主要アイディアは、 cost を符号反転して第 1 アイテムとしたタブルをヒープに入れること で、コストが高いほどタブルの値が ( thon の通常の比較によれば ) 小さくなること、またコストの直後に累 進インデックスを置いたため、等しいコストを持つ中では最初に加入したものが最小タブルとなることである。 thon2.4 で heapq モジュールは再実装され、最適化が行われた。 heapq に関するさらなる情報は「レシピ 5.8 シーケンスのアイテムを小さい方から取る」にある。 参照 ライプラリリファレンスおよび fPython クイックリファレンス』の heapq モジュールの項、 python ソース ツリーの he 叩 q. py には、ヒープに関する非常に興味深い考察がある、 heapq に関するさらなる情報は「レシピ こと 解法 はできるが、他にもっと良い方法はないだろうか。 シーケンスから最小のアイテムをいくつも取りたい。シーケンスをソートして seq [ : n ] する 問題 Credit: Matteo DeII' Amico, Raymond Hettinger, George Yoshida, Daniel Harding 訳 : 鴨澤眞夫 5.8 シーケンスのアイテムを小さい方から取る 5.8 シーケンスのアイテムを小さい方から取る」にある。 こうした用途に向いた、シンプルでプラクティカルなジェネレータを以下に オーダーとなってしまうのだ。 さければ、最小の n 個の要素を 0 ( n ) で取れるのに、 sort は ( とても速いのだが、それでも ) 0 ( n 10g n ) の時間 必要なアイテム数の n がシーケンス長の n に対して小さい場合は、もうちょっとうまくやれるはす。 n が小 巧 , thon2.4 オンリーだが、 n が事前にわかっている時、 n 個の最小データアイテムはさらにシンプルかつ高 yield heapq. heappop(data) while data: heapq. heapify(data) data = list(data) def isorted(data) : import heapq 示す。 Python 2.3 でも 2.4 でも同じように動く・ 考察 return heapq. nsmallest(), data) def smallest(), data) : import heapq 速に得られる。 data は結合された反復可能型であればよい。関数 isorted は data に対してます list を呼び出すことで、
194 ー 5 章サーチとソ ート ( 2 , 1 ) ) def lexcmp(sl, (2) : コードと等価である。 このコードは対応する項のうち、等しくない最初のものを探す。等しくないペアが見つかると、このペア return cmp(len(sl), len(s2)) # 片方あるいは両方のシーケンスが尽きるまで全て等しかったとき i + = 1 return outcome if outcome : outcome = cmp(s1[i],s2[i]) while i く len(sl) and i く len(s2): 1 # 最左の不等な組合わせを探す 後まで一致し、後者にさらに続きがある場合 ) 、前者を小さいシーケンスと考える。この 2 つの条件にあては が結論を決定する。また、もし一方のシーケンスが他方の正確な前置シーケンスとなる場合 ( 前者が後者と最 まらない場合は、すなわちシーケンスが同一ということなので、両者を等しいと考える。以下に例を示す : , 2 , 3 ) , ( 1 , 2 , 3 ) ) # 等しい 0 ” > cmp((l 1 ” > cmp((l -1 2 ) ( 1 , 3 ) ) # 後者が前置シーケンスなので前者が大きい # 1 く 2 なので前者が小さい # 1 = 1 、 2 く 3 なので前者が小さい 辞書順比較というものがあるため、第 1 のキーを比較に、第 2 のキーをタイプレイクに使ってオプジェク トのリストをソートしたい場合、第 1 キー、第 2 キー、元々のオプジェクトの順でこれらを格納したタブル のリストを作ってやり、これをソートするという方法がとれる。タブルは辞書順で比較されるので、正しい ことが自動で行われる。タブル同士を比べると、ます第 1 キーが比較され、第 1 キー同士が同一のときのみ 第 2 キーが比較される。 この章には DSU パターンの応用例が多数示してある。 DSU テクニックはキーがいくつあっても適用できる。 比較したい順で好きなだけキーを追加すればよいのだ。レシピの多くで指摘しているが、 thon2.4 では sort に追加されたオプション引数 key = を使うことで、同じ効果が得られる。 so て t メソッドの key = 引数は、いちい ち副リストを作るより簡単でメモリ効率が高く、しかも高速である。 巧 rthon 2.4 ではソートに関してもう 1 つイノヴェーションがあるが、これは便利なショートカットである。 ビルトイン関数 sorted がそれであり、あらゆる反復可能体を ( その場でではなく ) 、ます新しいリストにコピー してからソートするというものだ。 python 2.3 でもこの関数は簡単にコードできる ( ただし新しいオプション 引数 key = を除く。これは list. sort 同様、ビルトイン関数 sorted にも存在する ) :
] .25 CJn ⅸターミナル上で HTML をテキストに変換 125 Unix ターミナル上で HTML をテキストに変換 credit: Brent Burley, Mark Moraes 訳 : 鴨澤眞夫 解法 トしたい。 問題 HTML 文書を Unix ターミナル上にテキストとして表示したい。太字やアンダーラインもサポー 55 標準入力から HTML を取り、標準出力に制御シーケンス付きのテキストを吐く、フィルタースクリプトを 書くのが最もシンプルだ。レシピでは Un ⅸのみをターゲットとするので、ターミナル制御シーケンスは thon 標準ライプラリの os モジュール popen 関数から Unix コマンド tput を使って得ることができる : # ! /usr/bin/env python import sys, os, htmllib, formatter # Unix の tput を太字、アンダーライン、リセットのエスケープシーケンスを得るのに利用 = os. popen('tput b01d' ). read( ) set bold = os. popen('tput smul' ). read( ) set underline perform reset = os. popen('tput sgr0' ). read( ) class TtyFormatter(formatter. AbstractFormatter) : bold や italic のフォント状態を追跡し、これに応じた ターミナル制御シーケンスを出力するフォーマッタ init_(self, writer) : # ますはいつも通りスーノ←クラスの初期化 init (self, writer) formatter. AbstractFormatter. # bold でも italic でもなく font 状態もセープしてない状態で始める self. fontState = False, FaIse self. fontStack = def push font(self, font): # font タブルはアイテム 4 つだが italic と b01d 2 フラグのみ追跡 size, is italic, is bold, is tt = font self. fontStack. append((is italic, is bold)) self. updateFontState( ) def pop_font(self, *args): # 前の font 状態に戻る try: self. fontStack. pop( ) except IndexError: paSS self. updateFontState( ) def updateFontState(se1f) : # bold や italic ( = 下線 ) の状態に変化があれば # それに応じたターミナル制御シーケンスを出す def
4. い引用符におほれすティクショナリ構築ー 1 ハ 生 11 引用符におほれずティクショナリ構築 解法 くない。 キーが文字列リテラルであるディクショナリを構築したい。キーをいちいち引用符で囲みた 問題 credit: Brent Burley, peter C0g010 訳 : 鴨澤眞夫 考察 data = {'red': 1 , green ・ lu ざ・ data これは以下のディクショナリ表現 (dictionary-display) 構文による等価物よりよい・ = dict(red=l, green=2, blue=3) ときは、 dict を名前付き引数構文で呼ぶと、いちいち引用しなくてよい・ 巧 , thon でどんどんやっていくようになると、ディクショナリをやたらに使うものだ。キーが識別子である d = dict(itertools. izip(the keys, the values)) import itertools d 速だ。 クショナリとする。シーケンスが長いときは python 標準ライプラリの itertools モジュールを使う方が高 関数 zip は (key, value ) ペアのリストを返し、ビルトイン型 dict がこのリストを引数として受け取ってディ thekeys はキーのシーケンス、 thevalues はこれに対応した値による・・並列の " シーケンスだ。ピルトイン = dict(zip(the_keys, the values)) が使われる ) 。一般的なディクショナリ構築イディオムは以下の通り : value ) のシーケンスであっても有効なところが違う ( シーケンス中で key が複数回登場したときは最後のもの ディクショナリが返される。これは d. copy ( ) と同じだが、 dict ( d ) の場合 d がディクショナリでなくべア ( key , di ( t のコールには別の可能性も含まれている。 di ( t ( d ) とやると、既存ディクショナリ d とは独立の新しい えば空ディクショナリの構築に { } でなく di ( t ( ) が使えると嬉しいだろう。 中括弧の入力が大変なキーポード ( イタリア語レイアウトのやつはどれもこれも ! ) を使っているなら、たと ディクショナリ表示構文はまた thon で唯一、中括弧を必要とするものだ。中括弧がキライであるとか、 識別子にならないからだ。 列並び ' 12ba ' や・ fo て ' には使えない。 '12ba' は数字で始まり、 for は thon キーワードとなってしまうので 字列リテラル ) であるとき、 dict の呼び出しでキーの引用を避けられることを示した。このアプローチは文字 クショナリ表示構文よりよい場合は多い。このレシピでは、キーが thon 識別子として有効な文字列並び ( 文 ディクショナリ構築の強力な方法にビルトイン型 di ( t のコールがある。これが中括弧とコロンを使ったディ
4.1 2 def pairwise(iterable) : itnext = iter(iterable). next while True: yield itnext( ) , itnext( ) def dictFromSequence(seq) : return dict(pairwise(seq)) キーと値が交互に入ったリストから dict を構築ー 173 pa ⅱ wise を定義してしまったので、既存ディクショナリのアップデートにも、キーと値が交互になったシー ケンスを使えるようになった mydict. update(pairwise(seq)) と書けばよいのだ。 考察 pairwise の next メソッドを、他の言語に慣れたプログラマから見て自然な書き方に直しても、動作は同じだ : のメソッドを抽出、ローカル名に代入しておき、このローカル名を関数であるかのようにコールするのだ。 存在し、このオプジェクトに対してやりたいことが、ループ中での結合メソッドのコールのみであれば、そ と奇妙に思われるかもしれないが、 thon では一般性が高く、良いテクニックである。あるオプジェクトが をコールし、これにより得られる反復子の結合メソッド next を、ローカル名 itnext に結合している。ちょっ pa ⅱ wise はちょっとおもしろい実装になっている。第 1 文では、引数 iterable に対してビルトイン関数 iter 常に長かった場合のパフォーマンスが改善されるはずだ。 のペアのみを渡す。メモリ内に長いリストを構築するようなことは一切しないので、入力のシーケンスが非 ゆる反復可能型が使用可能なことを保証するようコーディングされている。もう 1 つ、 pairwise は一度に 1 つ いる。 pairwise はリストだけでなく、例えば他のジェネレータの出力やファイル、ディクショナリなど、あら もう一方の dictFromSequence は、ペアのシーケンスの構築タスクを pa ⅱ wise というジェネレータに委譲して ものになるかもしれない。 keysAndValues が長大なシーケンスであった場合、リスト構築によるパフォーマンスコストは、ちょっとした ラスのインスタンスでなければ動作しない。また、メモリ内に一時リストをいくつも構築することになる。 良いのだが、引数 keysAndVa1ues がリスト、タブル、 str など、拡張スライス形式をサポートした型またはク し、これらを引数に取ってビルトイン関数 zip をコールすることで、ペアのリストを作る。このアプローチは 2 , 4 , … ) 、もう 1 つは奇数インデックス ( 1 から始めて 2 すっカウントアップ ) のアイテムを集めたもの一一と dictFromList は関数自体の引数 keysAndVaIues を 2 つの拡張形式のスライスーー 1 つは偶数インデックス ( 0 , のように構築するかである。 のペアによるシーケンスを引数として、 dict をコールするのだ。違いは dict に渡すべきペアシーケンスをど どちらの " ファクトリー関数 " もディクショナリの構築に同じ内部機構を使っている。つまり (key, value) ではない ( 「 thon を知らない人から見て自然」は「シンプル」の同義語ではない ! ) 。そのうえ実際に約 60 % しかしこちらの pairwise slow は、「解法」で示した pairwise ジェネレータに比べ、実はまったくシンプル yield it. next( ) , it. next( ) while True : = iter(iterable) def pairwise slow(iterable) :
2.22 相対パスを導き出す一 97 ライプラリリファレンスおよび fPython クイックリファレンス』の sys 、 os. path 両モジュールに関する解 参照 解法 から別のディレクトリへの相対パスを知りたい。 たとえば、シンポリックリンクや URL における相対参照を作成するために、あるディレクトリ 問題 Credit: Cimarron TayIor, AIan Ezust 訳 : 吉宗貞紀、鴨澤眞夫 222 相対パスを導き出す 説。 pl と p2 に共通な部分が無ければ、 p20 = p2 の場合は、空のストリング。 pl から p2 への相対パスを返す。 def relpath(pl, p2, sep=os ・ path. sep, pardir=os. path. pardir) : return common, [ sequence[len(common) : ] fO て sequence in sequences ] # 共通の前半部分と、異なっている後半部分を返す common. append(elements[o]) # もう 1 つ共通の要素を取り込んで末尾に追加し、ループを続ける if not a11 equal(elements): break # 全ての要素が等しくない限りループを抜ける f0 て elements in itertools. izip(*sequences) : common # 各シーケンス上を平行してル - プする if not sequences: return [ ] , # そこにシーケンスがなけれは終了する シーケンスで異なっている後半部分のリストのリストを返す。 ー全てのシーケンスの先頭からの共通な要素を返し、次いで、それぞれの def common prefix(*sequences): return True if other element ! = first element: return False for other element in elements [ 1 : ] : first element = elements[o] ー要素が同一ならば True を、そうでない時は False を返す。 def a11 equal(elements) : import os, itertools 的なものと、もう少し一般的なものの 2 つの関数の助けを得て、次のようにコーディングできる : 最もシンプルなアプローチは、パスをディレクトリのリストに split してから作業する方法だ。ほんの補助
XXVi ー 5 章 6 章 目次 4.19 4.20 4.21 4.22 4.23 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 5.10 5.11 5.12 5.13 5.14 5.15 名前をソートしてイニシャルで分ける・・ ディクショナリにレーティング機能を・・ サプシーケンスを見つける・・ シーケンスにアイテムの存在チェックをかける・・・ クイックソートを 3 行で示す・・ n 番目に小さい要素は・・・ ソート済みシーケンスからアイテムを探索・・ シーケンスのアイテムを小さい方から取る・・ アイテムが加えられてもソート済みに保っ・・・ リストのアイテム全てをランダムな順序で処理・・ 含まれる数値で文字列をソート・・ キーやインデックスを、その値によりソート・・ オプジェクトのリストをオプジェクトの属性でソート・・ 文字列リストを大文字小文字無関係にソート・・ ディクショナリのソート・・ イントロダクション・・ サーチとソート・・ 指定のモジュール内で名前が定義されていることを保証する・・ 式中での例外処理・・ アイテムごとに確率を定めたランダム抽出・・ Python で printf ・・ 単一の文で代入と検査を同時に行う・・ オブジェクト指向プログラミング・・・ 6.0 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 6.10 6.11 6.12 イントロダクション・・ 温度スケールの変換・・ 定数を定義する・・ 属性の設定を制限・・・ ディレクトリ探索のチェーン化・・ 継承ではできないことをする ( 自動委譲 ) ・・ proxy で特殊メソッドを委譲・・ 名前付きアイテムのタブル・・ プロバティにアクセスするコードをキレイに オプジェクトのコピーを素早く・・ ガべージコレクションを邪魔することなく結合メソッドへのリファレンスを保持・・ インスタンスに変化があったかチェックする・・ リングバッフアを実装する・・ ・・ 185 ・・ 187 ・・ 188 ・・ 189 ・・・ 191 ・・ 1 93 ・・ 229 ・・・ 224 ・・・ 223 ・・・ 220 ・・・ 217 ・・・ 214 ・・・ 213 ・・ 211 ・・・ 209 ・・・ 207 ・・・ 206 ・・・ 203 200 ・・ 199 ・・ 198 ・・ 193 ・・・ 265 ・・・ 262 ・・・ 259 ・・・ 256 ・・・ 255 ・・・ 253 ・・ 250 ・・ 246 ・・・ 244 ・・・ 242 ・・・ 240 ・・・ 237 ・・・ 231 ・・・ 231