2010年9月21日火曜日

なぜPythonの割り算はC言語と違う方式なのか?

今日は何度も「なぜPythonの整数の割り算はC言語のようにゼロに向けて丸めるのではなく、切り捨てなのか?」と聞かれた。

数値が両方とも正の場合には、何も驚くようなことは発生しない。

>>> 5//2
2

しかし、どちらかの数値が負の場合、結果は切り捨てられる。言い換えると、ゼロから遠い方向(負の無限大方向)に向けて丸められる。

>>> -5//2
-3
>>> 5//-2
-3

この挙動は何人かの人を混乱させたようであるが、このような実装になっているのは数学的な根拠がある。整数の割り算の演算子(//)と、その弟の余りを計算する演算子(%)は、次のような数学的な関係を保っている。すべての変数は整数とする。

a/b = q あまり r

これは、次のような関係性を持っている。

b * q + r = a かつ 0 <= r < b
(ただし、a, bはともに0以上の整数)

もしも、この式の関係性を、負の数値まで拡張したいのであれば、2つの選択肢があります。もし商をゼロに向けて丸めるのであれば、付帯条件を0 <= abs(r)と変更する必要がある。それ以外には、qを負の無限大の方に向けて丸める方法がある。この場合、付帯条件は0 <= r < bのままである。

数学の整数論を考えると、数学者はいつも後者の選択を望ましいと考えてきた(Wikipedia参照)。この方式の割り算が役に立つ、おもしろい利用方法がいくつかあったので、Pythonでもこの後者の挙動を採用した。POSIXのタイムスタンプ(1970年からの経過の秒数)を取得して、時刻に変える、という場面を考えてみる。1日は24時間なので、24 * 3600 = 86400秒ある。これを計算するには、単にt % 86400とやれば良い。しかし、1970年依存のより前の時刻を計算したいと考えると、ゼロ方向に丸めてしまうと、無意味な結果が帰ってくることになる。負の無限大方向に丸めるというルールであれば、このような場面でも、そのまま適用できる。

コンピュータグラフィックスで、ピクセルの位置を計算する場面でも、同じ事が言える。こちらの方が良く発生するだろう。

もしbが負の値をであれば、すべてが逆さまになる。普遍条件は次のようになる。

0 >= r > b.

それでは、なぜC言語はこのような実装になっていないのだろうか?それは、Cが設計された当時のハードウェアがこのようにしていなかったからである。そして、現在では負の数は補数として表現されているが、当時のコンピュータは少なくとも整数に関しては、「符号+大きさ」という表現になっていた。私が最初に触れたコンピュータはデータを制御するメインフレームであったが、浮動小数点数と同じく、整数に補数を使うようになっていた。そのハードは負のゼロを表すパターンが60個もあったのである!

Pythonの浮動小数点数を完全に知り尽くしているTim Petersは、これらの規則を浮動小数点数の割り算にも広げたいという私の願望に対して、いくつか心配に思っていることを説明してくれた。彼の心配は正しかった。負の無限大の方向に丸めるというルールは、x%1.0という式があったときに、xが非常に小さい負の整数の時に精度が失われてしまうのである。しかし、これは整数の割り算を破壊するものではなく、//はそれに密に結合されている。

PS. 上記の説明では、/の代わりに//を使用した。これはPython 3の文法で、Python 2でも割り算が整数領域で行われるのを強調したいときに利用できる。Python 2の/演算子は、演算する数が両方整数か、一つ以上浮動小数点数が含まれているかで型が変わるという曖昧なところがある。しかし、これに関してはまったく別の話なので、ここでは説明しない。詳しくはPEP 238に書かれている。

2010年9月8日水曜日

リスト内包表記〜ジェネレータ式

リスト内包表記はPython 2.0で追加された。この機能はGreg Ewingによるパッチを元にして、Skip MontanaroとThomas Woutersらの貢献もあって実現された。私の記憶が正しければ、Tim Petersもこのアイディアをしっかりと保証してくれた。基本的には、リスト内包表記は数学者によって使用されてきた、よく知られた表記法をPythonicに解釈して実現したものである。

{x | x > 10}

これは、一般に、10より大きな数の集合と解釈される。数学の世界では、この形式は、例えば全ての実数、全ての整数などを表す普遍集合(universal set)であると、読み手は解釈する。Pythonの2.0には普遍集合の概念はなく、setもなかった。setについてもおもしろい話があるため、将来ブログに投稿することになるだろう。

この概念や、他の状況も踏まえ、次のようなPython流の表記が作られた:

[f(x) for x in S if P(x)]

これを実行すると、シーケンスSに含まれる値のうち、述部Pの評価結果に合う要素に対して、関数fを適用した結果を含むリストが生成される。if節はオプションとなっているし、また、for節を複数入れて、それぞれにif節を書いて、ネストされたループを表現することもできる。ただし、このネストされたループはあまり使われることはない。一般的には多次元の要素を、1次元のリストにマップすることが良く行われる。

リスト内包表記は、組み込み関数のmap()filter()の代替手段となっている。map(f, S)は、[f(x) for x in S]と同じ意味となるし、filter(P, S)[x for x in S if P(x)]と同じ意味となる。map()filter()を使った表現の方がコンパクトであるため、リスト内包表記の方が推奨されていないのでは?と思う人もいるだろう。しかし、より現実的なサンプルを見ると見解が変わるだろう。与えられたリストの全ての要素に数字の1が足された、新しいリストを作りたいとする。リスト内包表記を使った場合には、[x+1 for x in S]と表現できる。map()を使うと、map(lambda x: x+1, S)となる。"lambda x: x+1"はインラインで無名関数を作るPythonの書き方である。

ここでの本当の問題はPythonのラムダ記法が冗長すぎることで、この表記がもっと簡潔になればmap()を使った記法がより魅力的になるはずだ、ということがずっと主張されてきた。私個人の見解としてはこれには反対である。リスト内包表記の方が、特にマップされる式の複雑さが増加するとmap()を使った関数表記よりも見やすくなることが分かったからである。それに加えて、リスト内包表記を使った方が、実行速度の面からも、map()とラムダを使うよりも高速である。内部的に、ラムダ関数の呼び出し時には新しいスタックフレームが作られるオーバーヘッドがあるが、リスト内包表記の評価時には新しいスタックフレームが作られないからである。

リスト内包表記の成功を受けて、Python 2.4からは似たような表記であるが、実際にリストを生成せずにシーケンスを表現できる、ジェネレータ(これについては将来のエントリーで紹介する)が発明されることになった。この機能は「ジェネレータ式」と呼ばれる。

sum(x**2 for x in range(1, 11))

これは、引数にジェネレータ表現の引数を使用して、組み込みのsum()関数が呼ばれている。このジェネレータは、1から10までの数値の数の2乗を生成する。この関数は引数で生成された値をすべて合計して、385を返す。sum([x**2 for x in range(1, 11)])という表記と比べると、メリットは明らかである。リスト内包表記を使うと、すべての2乗の数を含むリストが実際に生成され、一度使われて破棄されてしまうのである。メモリ使用量が大きくなる巨大な配列の場合には、これも考慮すべきである。

リスト内包表記と、ジェネレータ表現の違いは小さいということも説明すべきだろう。例えば、Python 2では、次の表現は有効なリスト内包表記である。

[x**2 for x in 1, 2, 3]

しかし、次の表現はジェネレータ表現としては有効ではない。

(x**2 for x in 1, 2, 3)

"1, 2, 3"の部分にカッコを付けることで、きちんと動作させることができる。

(x**2 for x in (1, 2, 3))

Python 3では、これらのカッコは、リスト内包表記でも付ける必要がある。

[x**2 for x in (1, 2, 3)]

しかし、通常のforループや、明示的なforループの場合にはこれをキャンセルすることもできる。

for x in 1, 2, 3: print(x**2)

なぜ違いがあるのか、なぜPython 3.0ではリスト内包表記が厳格化されたのか?設計に影響を与えた要因としては、後方互換性、あいまいさの排除、等価性、言語の進化がある。初めPythonには、明示的なforループしかなかった。この場合には、'in'の後ろから、コロン(:)の前までであったため、あいまいなところはなかった。そのため、既知の値の上でループを回したいのであれば、煩わしくカッコを付ける必要はない。これを見ると、次のように書けた、Algol-60が思い出される。

for i := 1, 2, 3 do Statement

Algol-60はそれぞれの式の中に、step-until構文を利用することもできる。

for i := 1 step 1 until 10, 12 step 2 until
50, 55 step 5 until 100 do Statement

(これを振り返ると、Pythonのforループも、複数のシーケンス上でループが回せていたらもっとクールだっただろう)

Python 2.0にリスト内包表記を追加したときにも同じような推測法を適用した。シーケンスの式の後ろには閉じたブラケット']'、もしくは'for', 'if'キーワードのみが来るため問題が発生することはなかった。

しかし、ジェネレータ式が2.4で追加されると、あいまいさの問題が頭をもたげてきた。ジェネレータ式のまわりのカッコは、ジェネレータ式の一部ではないことも技術的にありえるからだ。次の例を見てみよう。

sum(x**2 for x in range(10))

外部のカッコは、sum()関数の呼び出しの一部であり、「裸の」ジェネレータ式が最初の引数として渡される。そのため、理論的には次の式は2通りの理解の仕方がある。

sum(x**2 for x in a, b)

これは、次の文を意図したものである。

sum(x**2 for x in (a, b))

このようにも理解できる。

sum((x**2 for x in a), b)

その後、活発な議論が行われ、(私の記憶が正しければ)このような場合には推測をしない、ジェネレータ内包表記の場合には、'in'キーワードの後ろには単一の式(もちろん繰り返し可能)を必要とする、ということになった。既に人気のある構文となっていた、リスト内包表記を使用していた既存のコードの互換性を崩したくない、という考えもあった。

Python 3の設計をしている時にリスト内包表記の動作に変更を加えた。次のようなリスト内包表記があったとすると、

[f(x) for x in S if P(x)]

組み込みのlist()関数をジェネレータ式に適用した、次の文と同一になるようにした。

list(f(x) for x in S if P(x))

このため、リスト内包表記でも、多少制約の強い、ジェネレータ式の構文を使用することを決定した。

Python 3ではもう一つ変更を加え、リスト内包表記とジェネレータ式の等価性が向上した。Python 2では、リスト内包表記のループ制御変数は周辺のスコープに「漏れ」ていた。

x = 'before'
a = [x for x in 1, 2, 3]

print x # this prints '3', not 'before'

これは、リスト内包表記の元本の実装によってもたらされた副作用である。これは昔からある、Pythonの「隠しておきたい小さな汚点」の一つであった。意図的な妥協のおかげで、リスト内包表記は最初から高速な実装であった。初心者がひっかかるような落とし穴ではなかったが、時々害を受ける人が出た。ジェネレータ式の実行には別の実行フレームが必要とされるため、ジェネレータ式では同じようなことはできなかった。そのため、特に小さなシーケンスを相手にする場合には、効率性の面でジェネレータ式はリスト内包表記に負けていた。

しかし、Python 3では、我々はジェネレータ式と同じ実装の戦略を取ることで、リスト内包表記の「隠しておきたい小さな汚点」を修正することを決定した。そのため、Python 3で上記のサンプルを実行すると(print(x)と変更を加えましょう :-)、'before'と表示され、リスト内包表記で一時的に隠された変数'x'も、周囲のスコープの'x'を上書きしないということが分かるだろう。

これによって、Python 3のリスト内包表記は遅くなったのではないか、と心配する人もいると思うが、Python 3を高速化しようという膨大な努力のおかげで、Python 3のリスト内包表記と、ジェネレータ式は、Python 2と比較してそれぞれ高速になっている。また、この2つの間にあった速度差も解消している。

更新: Python 3で追加された、setとdictの内包表記について言及するのを忘れていた。これらは、リスト内包表記を素直に拡張しただけである。

2010年8月1日日曜日

メソッド解決順序(MRO)

多重継承が利用可能な言語では、メソッドを探索するときのベースクラスを探索する順序を、メソッド解決順序(MRO)と呼んでいる。Pythonではメソッドだけではなく、属性の探索でもこれが利用される。単一継承しかサポートしていない言語では、MROはとてもつまらない話題であるが、多重継承の場合には必要となってきて、MROアルゴリズムの選択が極めて難しい問題となりうる。Pythonでは、クラシック、Python 2.2の新スタイル、Python 2.3の新スタイル(C3とも呼ばれる)の3種類のMROアルゴリズムを持っていることが知られている。Python 3では、最後のアルゴリズムだけが生き残っている。

旧スタイルクラスはシンプルなMROの方式を利用していた。メソッドを探す場合には、シンプルに深さ優先探索を行って、ベースクラスの中で最初にマッチしたオブジェクトが返される。例えば、次のようなクラス構造について見てみよう。

class A:
  def save(self): pass

class B(A): pass

class C:
  def save(self): pass

class D(B, C): pass

クラスDのインスタンスxを作ったとすると、クラシック方式のメソッド解決順序では、D, B, A, Cという順序でクラスが探索されることとなる。そのため、x.save()というメソッド呼び出しが行われると、C.save()ではなく、A.save()が実行される。この方式は単純なケースではうまく行くが、より複雑なケースでは問題が発生してくる。次のような、ダイアモンド継承下でのメソッド検索の問題について見てみよう。

class A:
  def save(self): pass

class B(A): pass

class C(A):
  def save(self): pass

class D(B, C): pass

ここでは、クラスDはBとCを継承しており、その両方ともがクラスAを継承している。クラシックMROを使うと、D, B, A, C, Aという順序でメソッドを探索していくため、x.save()は、A.save()を参照する。しかし、この場合は、あなたが期待するのと違っているはずだ。BとCの両方がAを継承しており、再定義されてクラスAよりも詳細化されたC.save()を、クラスAのメソッドよりも呼びたいと考える人もいるだろう。実際はA.save()が常に呼ばれる。例えば、save()メソッドがオブジェクトの状態の保存に使用されるのであれば、クラスCによって定義されたメソッドが呼ばれないことで、Cに関する状態が無視されてプログラムの動作に問題が生じる。

この種の多重継承は、既存のコードの中でもめったに使われていないが、新スタイルクラスを使うとこの問題が日常茶飯事となる。新スタイルクラスはベースクラスであるobjectを継承することで行われる。新スタイルクラスでは、どのように多重継承を行っても、必ず上記のようなダイアモンド継承状態になる。

class B(object): pass

class C(object):
  def __setattr__(self, name, value): pass

class D(B, C): pass

それに加えて、派生クラスで拡張されるような(__setattr__()など)、いくつかのメソッドをobjectクラスが持っているため、このメソッドの解決順序の重要性は以前よりも高くなった。例えば、上記のコードの場合には、C.__setattr__は、クラスDのインスタンスにも適用されるべきである。

Python 2.2では、新スタイルクラスの導入のためにメソッド解決順序の見直しを行い、クラス定義時にあらかじめ順序を計算しておき、クラスオブジェクトごとに属性として格納するという方式のMROを適用した。以前の公式ドキュメントでは、深さ優先探索のMROを利用していると書かれていた。この検索順序の中で同じクラスが重複すると、MROのリストの中で後半に出てきた方が削除されるようになった。そのため、前に挙げたサンプルでは、D, B, C, Aという順序になり、以前のクラシッククラスで説明したD, B, A, C, Aとは違う結果になる。

実際には、MROの計算はこれよりもはるかに複雑である。私は、この新しいMROアルゴリズムがうまく働かないようなケースをいくつか発見した。2つのベースクラス(A, B)が、2つの異なる派生クラス(X, Y)のMROのリストの中でそれぞれ異なる順序で並ぶという特殊なケースがある。これらのクラスは元のクラスをそれぞれ異なる順序で継承しており、さらにそれを他のクラス(Z)が継承している。

class A:
class A(object): pass
class B(object): pass
class X(A, B): pass
class Y(B, A): pass
class Z(X, Y): pass

試しにこの新しいMROアルゴリズムを使用してみると、これらのクラスのMROは、Z, X, Y, B, A, objectという順序に並ぶ。objectというのは、共通のベースクラスである。しかし、私はBとAが逆になっているというのが気に入らなかった。本当のMROは、それらの順番を入れ替えて、Z, X, Y, A, B, objectという順番にする。このアルゴリズムは最初に検索したときに見つかった順番をなるべく保存しようとする。クラスZの場合には、継承リストの順番が先ということで、ベースクラスXが先に検索される。XはAとBを継承しているため、MROアルゴリズムはこの順番を保存しようとする。これはPython 2.2で実装されたもので、初期のアルゴリズムとしてドキュメント化した。

しかし、Python 2.2で新スタイルクラスが導入されてすぐに、Samuel Pedroni氏は、このドキュメント化されたMROアルゴリズムと、実際にコードを動かした結果が異なるということを発見した。さらに、これらの矛盾は上記のような特別な場合以外でも発生していた。長い時間をかけて議論を行い、Python 2.2で採用されたMROは壊れていることが確認され、"A Monotonic Superclass Linearization for Dylan" (K. Barrett, et al, presented at OOPSLA'96) で説明されている、C3線形化アルゴリズムを採用することが決定された。

本質的には、Python 2.2のMROアルゴリズムの本質的な問題は、探索順を線形に並べるという問題に関係していた。継承階層が複雑になると、それぞれの継承の関係は、シンプルなルールでクラスがどのような順番に並ぶかのチェックが行われる。明らかに、クラスAがクラスBを継承している場合にはMROはAがBの前にあることをチェックしなければならない。同様に、クラスBがクラスCとDを多重継承している場合には、BはCの前にあり、CはDの前にあることがチェックされなければならない。

複雑な継承階層の中では、順番を線形化する場合に、これらのルールをすべて満足するようにしたいと思うだろう。つまり、クラスAがクラスBの前になければならないということが決められた場合に、クラスBの方がクラスAの前に来るという矛盾した状態にはなって欲しくはない。その場合には結果が未定義なので、そのような継承階層はリジェクトされなければならない。オリジナルのMROが間違っていて、C3アルゴリズムがうまくいくのはこのような場面である。基本的に、C3の背後にあるアイディアは、複雑なクラス継承を行ったときにも、継承の関係から作られた順番のルールをすべてリストアップした場合に、すべてのクラス間でこれらのルールを満足するようなクラスの順番に、一列に並べることができるというものである。もしも、順番が一意に決まらない場合には、このアルゴリズムはその継承をエラーとして失敗させるようになっている。

そのため、Python 2.3では、Python 2.2で自前作ったMROアルゴリズムを捨て、アカデミックな目でチェックされたC3アルゴリズムを選択することにした。これにより、矛盾した順序を持つ継承を行うと、Pythonがそれをエラーとするようになった。例えば、先ほどの例で言えば、クラスXとクラスYの間では、順序に矛盾がある。クラスXを見るとクラスBの前にクラスAがなければならないということになるが、クラスYを見ると、クラスAの前にクラスBがなければならないことになっている。個別に見れば、これらの矛盾は問題ないが、これらのクラスXとYが、同じ継承階層の中に現れる(例えば、ここではクラスZが定義されることによってこれが発生する)と、C3アルゴリズムはこのクラスをリジェクトする。これは言うまでもなく、Pythonのthe Zen of Pythonの「問題があるときには、こっそりと処理してはならない」というルールにもマッチしている。

2010年7月17日土曜日

import antigravity

原文:http://python-history.blogspot.com/2010/06/import-antigravity.html
原文投稿者:Guido van Rossum

Pythonについて説明をしているXKCDコミックを参照するというantigravityモジュールが、Skip MontanaroによってPython 3に追加された。私が知る限り最初にこのモジュールについて焦点をあてたブログはこれである: http://sciyoshi.com/blog/2008/dec/30/import-antigravity/

しかし、このモジュールの本当の起源はGoogle App Engineである。私たち(訳注: GuidoはGoogleで働いている)がApp Engineを公開した、2008年4月7日の直前だった。公開の数週間前の、ほとんどのコードがコードフリーズしたときに、GoogleのApp Engineチームは何かイースターエッグを忍び込ませたいと考えた。時には複雑で、時にはあいまいで、時にはリスキーな、何度にも渡るすばらしい議論の末に、"antigravity"モジュールが選択された。App Engine版はPython 3版よりも多少がんばった作りになっている。App Engine版では、fly()関数が定義されていて、次の2つのうちの一方の動作をランダムにランダムに行う。1つ目は10%の確率で発生するモノで、XKCDのコミックにリダイレクトでジャンプする。もう一方はHTML内に、テキスト版のコミックを単純に書き出すだけのものである(最後の行にコミックへのリンクが表示される)。次のようなmain()プログラムを作成すると、App Engineのアプリケーション内でこれを呼び出すことができる。

import antigravity

def main():
    antigravity.fly()

if __name__ == '__main__':
    main()

[更新情報] Python 3の標準ライブラリ版では、ソースコードの中を見てみると、イースターエッグの中にさらにイースターエッグが仕込まれている。XKCDのgeohashアルゴリズムについて説明している関数が定義されている。

2010年7月13日火曜日

import thisとThe Zen of Python(2)

原文:http://www.wefearchange.org/2010/06/import-this-and-zen-of-python.html
原文投稿者:Barry Warsaw

注:本エントリーは、Guidoのブログではなく、そこで紹介されていた、Barry Warsaw氏のブログの翻訳です。翻訳と公開の許可をしてくださったBarry Warsaw氏に感謝します

Richard Jones氏は現在(元記事執筆時点)、PyCon Australiaでする話の準備をしている。彼はその準備の中で、私に"Zen of Python"の歴史についてたずねてきた。それはTim PetersがPython界に永遠に残した言葉で、Pythonの本質的な真実を良く伝えるものであり、さまざまな場面で引用されている。最初に調べたときには、このリストが最初に公表された時の情報を探すことはできなかったが、その後、自分のメールのアーカイブをすみずみまで調べたところ、"Zen of Python"が最初にPython-listというメーリングリストに投稿された時のメールを見つけることができた。"The Python Way"というタイトルで、1999年の7月4日に投稿されたものであった。

すぐに最初の出典を見つけることができなかったので、自分のアーカイブの探査に戻ったり、"this"モジュールの解析を行っていた。最近のPythonのインタプリタでは、次のようにタイプすると、"Zen of Python"を読むことができるというのをご存知だろうか?(訳注:日本語訳はこちら)

% python
Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

"import this"の背景の話は楽しいものなので、私は、これが追加された経緯について紹介したら面白いだろうと考えた。おそらく、Guidoの"History of Python"というブログに何かストーリーを追加するような内容になるはずである。

2001年の秋に、ForetecInternational Python Conference #10 (IPC 10, PyConの先駆けとなるイベント)の開催準備をしていた。ForetecはCNRIの子会社のイベント運営会社で、Pythonlabsの設立のために退社する2000年まで、Guido, Fred,Jeremyと私を雇っていた。その会社は動きが悪いこともあったが、好意的な面も多数ある会社であった。その時には既に、私たちは友人たちと一緒にZope Corporation(おそらく当時はまだDigital Creationsという名前)で働いていたが、ForetecはIPCの開催を継続していた。ForetecはカンファレンスのスローガンをTシャツにプリントしたいと考えており、Pythonコミュニティからスローガンの提案を集めようと考えていた。提案されたエントリーを審査して、勝者を決めるというタスクを行うことをPythonlabsは同意した。しかし、私の記憶では、当時奥さんの出産を控えていたGuidoには、エントリーを審査する時間とエネルギーがなかった。

私たちは500ほどのエントリーを受けつけたが、ほとんどはひどいものであった。Timは「Louise、5分も見ていると、脳が溶け出しそうになるよ」というようなことを言っていたが、なんとか作業を継続して、130エントリーほどになるまでカットした。最後の勝者が決まる、最後の1分まで、私たちはただただ気が狂いそうだった。Timは私に、リストを交換する提案をしました。リストを半分にして、半分だけ交換しました。Timは私よりも遥かに数学が得意であり、Pythonの数値の割り算のくせを忘れていたため、最後の2つのエントリーが抜けてしまった。それらが「好きなものはすべて食べられます。咀嚼するのはオプションです」(私には良いところがまったくないと思われた;)と、「import this」であった。Timは「Pythonのプログラムに関して学ぼう」というエントリーについて二人ともが良いと思っていた土壇場になって、このおもしろいものを復活させた。

私はこの「import this」という偉そうなセンテンスを気に入った。また、このセンテンスを利用すればマイケル・ジャクソン風のすばらしいTシャツが作れそうだという可能性を感じた。

"import this"を選択してすぐに、それをプログラムとして実装した方がいい、ということに気づいた。Python 2.2のリリースが間近だったため、私は、コードのチェックインを知らせる機能をオフにして"this.py"というthe Zen of Pythonを表示させるモジュールを忍び込ませてはどうか?という提案をした。TimかGuidoのどちらかが、内容を難読化するのにrot13という暗号化をしてみたらどうか?と提案した。その仲間内以外の人には誰にも話しをしなかった。Googleを探索して調べたところ、IPC 10が終了してすぐに、私たちはPython 2.2.1としてthis.pyをコミットして、このイベントを祝していた。つまり、2.2.1には、本来リリースすべき真面目な機能に加えて、このジョークの機能が追加されていたのである。私の記憶が正しければ、ここで仕込んだ小さなイースターエッグが発見されるまでには、多少の時間を要した。

これは、Pythonコミュニティにユーモアがあった時代を思い出させてくれるできごとである。