2012年3月26日月曜日

デザインと歴史 FAQ — Python 2.7ja1 Documentation


Python はなぜ文のグループ化にインデンテーションを使うのですか?

Guido van Rossum の信じるところによれば、インデントによるグループ化は非常にエレガントで、平均的な Python プログラムを大いに読みやすくします。しばらくすればほとんどの人はこの特徴を気に入るようになります。

begin/end の括りがないので、構文解析器と人間の読者の間にグループ化の解釈の違いは起こりえません。時折、C のプログラマはこのようなコード片に出くわします:

if (x <= y)         x++;         y--; z++;

この条件文が真の時のみ実行されるのは x++ 文だけですが、このインデンテーションでは誤解を招きます。経験を積んだ C プログラマでさえ、 yx > y の時にもデクリメントされるのはなぜか分からず長いこと立ち止まることがあるでしょう。

begin/end の括りがないので、Python はコーディングスタイルの対立が非常に起こりにくくなります。C では多様なカッコの置き方があります。一つのスタイルでのコードの読み書きに慣れてしまうと、他のスタイルを読むとき (あるいは書かなくてはならないとき) にむずむずするでしょう。

多くのコーディングスタイルは begin/end の括りにそれぞれ一行を使います。これではプログラムは冗長になって画面を浪費し、プログラムの見通しが悪くなります。一つの関数は一画面 (例えば 20 - 30 行) に収めるのが理想です。 20 行の Python は20行の C よりもはるかに多くのことができます。これは begin/end の括りがないからだけではありません – 宣言が不要なことや高レベルなデータ型もその理由です – が、インデンテーションに基づく構文は確かに役に立っています。

なぜ単純な算術演算が奇妙な結果になるのですか?

次の質問を参照してください。

なぜ浮動小数点演算はこれほど不正確なのですか?

このような結果は、よく驚かれたり Python のバグであると考えられたりします:

>>> 1.2 - 1.0 0.199999999999999996 

でもこれはバグではありません。これは Python ではなく、その基底にある C のプラットフォームによる浮動小数点数の扱い方の問題で、究極には数を固定長の桁に書き下す際に生じたものです。

浮動小数点数の内部表現では一定数の二進数で十進数を示します。二進数では正確に表せない十進数もあり、僅かな丸め誤差を生じます。

十進数演算では、1/3 = 0.3333333333....... など、固定長の十進数では表せない数がたくさんあります。

基数が 2 のとき、1/2 = 0.1、1/4 = 0.01、1/8 = 0.001、などになります。 .2 は 2/10 と等しく、1/5 と等しいので、二進数の分数で 0.001100110011001... になります。

浮動小数点数には 32 か 64 ビットの精度しかないので、ある桁で切り捨てられ、十進数表示で 0.2 ではなく 0.199999999999999996 となります。

浮動小数点数の repr() 関数はすべての浮動小数点数 f に対して eval(repr(f)) == f が真となるのに必要なだけの桁を表示します。 str() 関数はそれより少ない桁を表示するので、より意図を汲んだ感覚的な数を得やすいです:

>>> 1.1 - 0.9 0.20000000000000007 >>> print 1.1 - 0.9 0.2 

その結果、== による浮動小数点の演算結果の比較は間違いやすいです。僅かな不正確さだけで == が間違うこともあります。その代わりに、二つの数間の差があるしきい値よりも小さいことを調べなくてはなりません:

epsilon = 0.0000000000001  # Tiny allowed error expected_result = 0.4  if expected_result-epsilon <= computation() <= expected_result+epsilon:     ... 

詳しくは、Python チュートリアルの floating point arithmetic の章を参照してください。

なぜ Python の文字列はイミュータブルなのですか?

これにはいくつかの利点があります。

一つはパフォーマンスです。文字列がイミュータブルなら、生成時に領域を割り当てることができるので、必要な記憶域は固定されて、変更されません。これはタプルとリストを区別する理由の一つでもあります。

別の利点は、Python で文字列が数と同じくらい "基本的" なものと考えられることです。8 という値を他の何かに変える手段が無いように、文字列 "eight" を他の何かに変える手段も無いのです。

なぜメソッドの定義や呼び出しにおいて 'self' を明示しなければならないのですか?

このアイデアは Modula-3 から取り入れられました。これは様々な理由からとても便利だと言えます。

まず、扱っているのがローカル変数ではなく、メソッドやインスタンス属性なのだと分かりやすいです。self.xself.meth() と書いてあれば、そのクラスの定義を憶えていなくても、それがインスタンス変数やメソッドであることは明白です。C++ では、(グローバルは滅多にないし、簡単に判別できるので) ローカル変数宣言されていないことからある程度わかるでしょう。– しかし Python にはローカル変数宣言がないので、クラス定義を調べて確かめなくてはなりません。C++ や Java のコーディングスタンダードに、インスタンス属性に m_ 接頭辞をつけるものがあるように、この明示性はそれらの言語でも役に立ちます。

第二に、特定のクラスからメソッドを明示的に参照や呼び出ししたい時に、特別な構文が必要なくなります。C++ では、派生クラスでオーバーライドされた基底クラスからメソッドを使うには、:: 演算子を使わなければなりません。 – Python では、baseclass.methodname(self, list>) と書けます。これは特に、__init__() メソッドに便利ですし、派生クラスのメソッドが、基底クラスにある同じ名前のメソッドを拡張するために、基底クラスのメソッドをどうにかして呼び出したい時にも便利です。

最後に、インスタンス変数の、代入の構文の問題を解決できます。 Python のローカル変数は、関数の中で (global が明示的に宣言されることなく) 値が代入された変数 (と定義されています!) なので、インタプリタには、代入がローカル変数にではなくインスタンス変数にされたのだと判断する方法が必要で、構文を見るだけで分かる方が (効率が) 良いのです。 C++ ではその区別を宣言時に行いますが、Python では宣言がないので、この方法でしか区別できなかったら残念です。self.var を明示すればしっくりきます。同様に、インスタンス変数を使うためにも self.var と書かなければならないので、メソッドの中の self が付いていない名前への参照は、そのインスタンスのディレクトリを検索するまでもなくローカル変数とわかります。別の言い方をすれば、ローカル変数とインスタンス変数は二つの異なる名前空間に存在し、Python にどちらの名前空間を使うかを伝えなくてはならないのです。

式中で代入ができないのはなぜですか?

C や Perl に慣れた多くの人は、C のこの慣用句を使いたいと訴えます:

while (line = readline(f)) {     // do something with line } 

Python ではこう書かなくてはなりません:

while True:     line = f.readline()     if not line:         break     ... # do something with line 

Python の式中での代入を許さない理由は、この構造によって起こる他の言語ではありがちで見つけづらいバグです:

if (x = 0) {     // error handling } else {     // code that only works for nonzero x } 

このエラーは単純なタイプミスで、 本当にやりたかったのは x == 0 の比較ですが、x = 0 と書いてしまい、変数 x に 0 を代入しています。

提案された代替案はたくさんあります。多くの案はタイプ数を少し節約しますが、勝手だったり意味不明だったりする構文や予約語を使い、言語変更の提案の簡潔さの基準を満たしていません。構造の説明をされていない人間の読者に、正しい意味を直感的に示す物であるべきです。

面白いことに、熟練した Python プログラマは while True というイディオムを受け入れていて、式構造中の代入がなくてもそれほど苦労しないようです。 Python にそれを強く求めるのは新人だけです。

以下の方法でもこれを綴ることができて、魅力的そうですが、堅牢さでは "while True" を使う方法に劣ることが多いです:


誰がビデオテープレコーダーを発明した
line = f.readline() while line:     ... # do something with line...     line = f.readline() 

この方法の問題は、次の行を取得する方法を変えたくなったとき (sys.stdin.readline() に変更したい時など) にプログラムの二箇所を変えなくてはならないことです – 二つ目の場所はループの最後に隠れています。

一番いいのはイテレータを使って、 for 文でオブジェクトを通してループさせることです。例えば、ファイルオブジェクトはイテレータプロトコルをサポートしているので、単純にこう書けます:

for line in f:     ... # do something with line... 

Python にメソッドを使う機能 (list.index()等) と関数を使う機能 (list.index()等) があるのはなぜですか?

歴史上の経緯が主な理由です。関数は型のグループに共通で、メソッドを持たないオブジェクト(タプル等)にも適用できるようにした操作に使われていました。オブジェクトの無定形な集合に容易に適用できる関数があることは、Python の関数的機能 (map()apply() 等) を使うときにも便利です。

実際、len()max()min() を組み込み関数として実装することで、それぞれの型のメソッドとして実装するより少ないコードで済みます。個々のケースについては粗探しのしようがありますが、Python の一部であるし、根本的な変更をするには遅すぎます。これらの関数は、大規模なコードの破壊を避けるために残す必要があります。

ノート

Python の文字列演算は、外部の関数からメソッド (string モジュール) に移行しました。しかし、len() は関数のままです。

join() がリストやタプルのメソッドではなく文字列のメソッドなのはなぜですか?

文字列は Python 1.6 からメソッドが追加され、他の標準型と同じような機能が string モジュールの関数でいつでも使えるようになったことで、他の標準型に大きく近づきました。その新しいメソッドの多くは広く受け入れられましたが、一部のプログラマに不快を感じさせていると思われるものがこれで:

", ".join(['1', '2', '4', '8', '16']) 

結果はこうなります:

この使い方には二つの議論があります。

一つ目は、「文字列リテラル (文字列定数) のメソッドを使うのは醜すぎる」というようなものです。確かにそうかも知れませんが、文字列リテラルは単なる固定された値に過ぎないというのが答えです。文字列に束縛された名前にメソッドが許されるなら、リテラルに使えないようにする理由はないでしょう。

二つ目の反対理由は、典型的には「シーケンスを文字列定数で結合させようとしているのだ」というものです。残念ながら、そうではないのです。いくつかの理由から split() を文字列のメソッドとしておいた方がはるかに簡単です。これを見ると分かりやすいでしょう:

"1, 2, 4, 8, 16".split(", ") 

これは文字列リテラルに与えられた分離子 (デフォルトでは空白文字) によって区切られた部分文字列を返すように指示しています。このとき、Unicode 文字列は Unicode 文字列のリストを返し、ASCII 文字列は ASCII 文字列のリストを返すから、みんな幸せです。

join() は、セパレータ文字列に、文字列のシーケンスをイテレートして隣り合う要素の間に自身を挿入するように指示しているので、文字列のメソッドです。このメソッドは、独自に定義された新しいクラスを含め、シーケンスの規則を満たすいかなる引数にも使えます。

これは文字列メソッドなので、Unicode 文字列にも 通常の ASCII 文字列にも使えます。 join() がシーケンス型のモジュールだったとしたら、そのシーケンス型はどちらの型の文字列を返すか、セパレータの型によって決めなければなりません。

例外はどれくらい速いのですか?

try/except ブロックは極端に効率がいいです。実際に例外を補足するのは高価です。 Python 2.0 より前のバージョンでは、このイディオムが一般的でした:

try:     value = mydict[key] except KeyError:     mydict[key] = getvalue(key)     value = mydict[key] 

これは、辞書がほとんどの場合にキーを持っていると予想できるときにのみ意味をなします。そうでなければ、このように書きます:

if mydict.has_key(key):     value = mydict[key] else:     mydict[key] = getvalue(key)     value = mydict[key] 

ノート

Python 2.0 以降では、value = mydict.setdefault(key, getvalue(key)) のように書くことができます。

Python に switch や case 文がないのはなぜですか?

if... elif... elif... else の繰り返しで簡単に同じことができます。 switch 文の構文に関する提案が幾つかありましたが、範囲検定をするべきか、あるいはどのようにするべきかについての合意は (まだ) 得られていません。現在の状況の完全な詳細は PEP 275 を参照してください。

非常に大きな数の選択肢から選ぶとき、値を呼び出す関数に対応づける辞書を作れます。例えば:

def function_1(...):     ...  functions = {'a': function_1,              'b': function_2,              'c': self.method_1, ...}  func = functions[value] func()

オブジェクトのメソッドを呼び出すには、さらに単純に getattr() 組み込み関数で特定の名前のメソッドを検索することが出来ます:

def visit_a(self, ...):     ... ...  def dispatch(self, value):     method_name = 'visit_' + str(value)     method = getattr(self, method_name)     method() 

メソッドの名前にこの例の visit_ のような接頭辞を使うことを勧めます。このような接頭辞がないと、信頼できないソースから値が与えられたときに、オブジェクトの任意のメソッドを呼び出す攻撃をされる可能性があります。

OS 特有のスレッド実装に依らずにインタプリタでスレッドをエミュレートすることはできないのですか?

答 1: 残念なことに、インタプリタは Python のスタックフレームごとに少なくとも一つの C のスタックフレームを push します。同様に、拡張もほとんどランダムなときに Python にコールバックすることがあります。よって、完全なスレッド実装には C のスレッドサポートが必要です。

答 2: 幸運なことに、完全に C スタックを使わないように再設計されたインタプリタ Stackless Python があります。まだ実験的なものですが、将来性がありそうです。これは標準の Python とバイナリ互換ですが、スタックレスの中核となるかどうかはまだわかりません – これはあまりに革命的すぎるかもしれません。

ラムダ式が文を含めないのはなぜですか?

Python の構文的な枠組みでは式の中にネストされた文を扱えないため、 Python のラムダ式は文を含めません。しかし、Python ではこれは深刻な問題ではありません。他の言語でのラムダ式が機能性を追加するものであるのと違い、Python でのラムダは関数を定義するのが面倒なときの速記法に過ぎません。

Python では関数はもとからファーストクラスオブジェクトであり、ローカルなスコープで宣言できます。よって、ローカルに宣言される関数ではなくラムダ式を使う利点はただ一つ、関数の名前を考えなくてもいいことです - でもその関数オブジェクトが代入されるのは (ラムダ式が名前を空けたのとまさに同じ型のオブジェクトである) ローカル変数です!


ミックスと一致するとは何か

Python は C やその他の言語のように機械語にコンパイルできますか?

簡単にはできません。Python の高水準データ型、動的な型付け、(eval()exec() を使った) インタプリタの実行時呼び出しがあるということは、「コンパイルされた」Python のプログラムのほとんどが、x+1 のような一見簡単な演算でさえ、Python のランタイムシステムへの呼び出しで成り立っているであろうことを意味します。

Python ニュースグループや過去の Python conferences で説明されたいくつかのプロジェクトを見ると、現在の速度の向上は緩やかでしかない (たとえば 2 倍) ですが、このやり方はうまくいきそうです。 Jython は Java バイトコードにコンパイルするという同様の方法を使っています。 (Jim Hugunin の論証によれば、プログラム全体の解析と組み合わせることで、小さなデモプログラムでは 1000 倍の速度向上が見込めます。詳しくは 1997 Python conference の議事録を参照してください。)

内部的には、Python のソースコードはいつもバイトコード表現に翻訳されていて、そのバイトコードが Python の仮想マシンによって実行されます。めったに変更されないモジュールの解析が繰り返されることによるオーバーヘッドを避けるため、このバイトコードはモジュールが解析されるたびに名前が ".pic" で終わるファイルに書きこまれます。対応する .py ファイルが変更されたとき、そのファイルは再び解析および翻訳されて .pyc ファイルは書き直されます。

一旦 .pyc ファイルが読み込まれればパフォーマンスの差はなく、 .pyc ファイルから読み込まれたバイトコードも、直接の変換により生成されたバイトコードも全く同じです。唯一の違いは、.pyc ファイルからコードを読み込むのは .py ファイルを解析して翻訳するのよりも速いことなので、予めコンパイルされた .pyc ファイルがあると Python スクリプトの起動時間が改善します。必要なら、 Lib/compileall.py モジュールで、与えられたモジュール群の適切な .pyc ファイルを生成できます。

なお、Python によって実行されるメインスクリプトは、たとえそのファイル名が .py で終わっていても、.pyc ファイルにコンパイルされません。バイトコードには変換されますが、そのバイトコードはファイルに保存されません。たいていメインスクリプトはとても短いので、これでも大きく速度を落とすことにはなりません。

Python と C のコードを様々な方法で混合して簡単にパフォーマンスを向上させるプログラムがいくつかあります。例えば Psyco、 Pyrex、 PyInline、 Py2Cmod、 Weave を参照してください。

Python はメモリをどのように管理するのですか?

Python のメモリ管理の詳細は実装に依ります。Python の標準の C 実装は参照カウントを使って、アクセスできないオブジェクトを探します。また別のメカニズムも使って参照サイクルを集めます。これはサイクル検出アルゴリズムを定期的に実行し、アクセスできないサイクルを探し、それに含まれるオブジェクトを削除します。gc モジュールの関数で、ガベージコレクションを実行し、デバッグ統計を取得し、コレクタのパラメタを変更できます。

Jython は Java ランタイムに頼るので、JVM のガベージコレクタが使われます。 Python のコードが参照カウントの実装の振る舞いに依るとき、この違いが微妙な移植問題を起こすことがあります。

循環性がなければ、Python プログラムはメモリを明示的に管理する必要はありません。

なぜ Python は伝統的なガベージコレクション体系を使わないのでしょうか?まず、それは C の標準的な機能ではないのでポータブルではありません。 (Boehm GC を例に取りましょう。これには most 有名なプラットフォームのためのアセンブリコードが含まれますが、全てには対応していませんし、ほとんど transparent ですが、完全に transparent ではありません。 Python を対応させるにはパッチが必要です。)

伝統的な GC は Python が他のアプリケーションに実装されるときにも問題となります。スタンドアロンの Python で動く限りでは、標準の malloc() と free() を GC ライブラリから提供されるものに置き換えても問題ありませんが、Python を実装したアプリケーションは Python のものではない 独自の 代替品を使おうとするかもしれません。現在のようにすることで、Python は malloc() と free() が適切に実装されている限りどんなものにも対応させられます。

Jython では、以下の (CPython では通る) コードはおそらく、メモリを使い切るより遥かに前にファイルディスクリプタを使い果たすでしょう:

for file in very_long_list_of_files:     f = open(file)     c = f.read(1) 

現在の参照カウントとデストラクタのスキームを使えば、 f への新しい代入ごとに前のファイルは閉じられます。GC を使うのでは、これは保証されません。どんな Python の実装にも適用できるコードを書くには、明示的にファイルを閉じるか、with 文を使いましょう。これは GC に関係なく働きます:

for file in very_long_list_of_files:     with open(file) as f:         c = f.read(1) 

なぜ Python の終了時にすべてのメモリが開放されるわけではないのですか?

Python モジュールのグローバルな名前空間から参照されるオブジェクトは、 Python の終了時にメモリの割り当てを解除されるとは限りません。これは、循環参照があるときに起こりえます。開放できない C ライブラリ (例えば、Purify のようなツールなどが当てはまります) によって割り当てられたいくらかのメモリも含まれます。しかし、Python は終了時にメモリをクリーンアップすることには積極的で、全ての各個オブジェクトを破棄しようとします。

再割り当て時に Python が特定のものを削除するように強制したいときは、 atexit モジュールを使って削除を強制する関数を実行してください。

なぜタプルとリストという別のデータ型が用意されているのですか?

リストとタプルは、多くの点で似ていますが、一般には本質的に異なる方法で使われます。タプルは、Pascal のレコードや C の構造体と同様なものと考えられます。型が異なっても良い関連するデータの小さな集合で、グループとして演算されます。例えば、デカルト座標は 2 つや 3 つの数のタプルとして適切に表せます。

一方、リストは、もっと他の言語の配列に近いものです。全て同じ型の可変数のオブジェクトを持ち、それらが一つ一つ演算される傾向にあります。例えば、os.listdir('.') はカレントディレクトリ内にあるファイルの文字列表現のリストを返します。この出力を演算する関数は一般に、ディレクトリに一つや二つの別のファイルを加えても壊れません。

タプルはイミュータブルなので、一度タプルが生成されたら、そのどの要素も新しい値に置き換えられません。リストはミュータブルなので、リストの要素はいつでも変更できます。イミュータブルな要素だけが辞書のキーとして使えるので、リストではなくタプルだけがキーとして使えます。

リストはどのように実装されているのですか?

Python のリストは真の可変長配列であり、Lisp スタイルの連結リストではありません。この実装は、他のオブジェクトへの参照の連続した配列を使い、リストの頭部構造にこの配列へのポインタと配列の長さを保持します。

これにより、リストのインデクシング a[i] は、リストの大きさやインデクスの値に依存しないコストで演算できます。

要素が追加または挿入されるとき、この参照の配列は大きさが変更されます。要素追加の繰り返しのパフォーマンスを上げるために、少し工夫されています。配列が大きくなるとき、次の何回かは実際に大きさを変更する必要がないように、いくらかの追加の領域が割り当てられます。

辞書はどのように実装されているのですか?

Python の辞書は大きさを変更できるハッシュテーブルとして実装されています。 B 木にコンパイルされることで、ほとんどの条件下で (特に一般的な演算である) 探索のパフォーマンスが良くなりますし、実装も単純です。

辞書は、 hash() ビルトイン関数で、辞書に保存されているそれぞれのキーに対応するハッシュコードを計算して働きます。このハッシュコードはキーに大きく依存します。例えば、"Python" のハッシュ値は -539294296 ですが、ビットが一つ違うだけの文字列 "python" のハッシュ値は 1142331976 です。そしてこのハッシュコードは、内部配列での値が保存される位置を計算するために使われます。保存しているキーのハッシュ値が異なるとすれば、一定の時間 - コンピュータサイエンスの記法で言えば O(1) - でキーを検索できることになります。また、キーのいかなる並び順も保たれていないことにもなり、配列を .keys().items() として横断すると、辞書の内容が任意の混乱した順序で出力されます。

なぜ辞書のキーはイミュータブルでなくてはならないのですか?

辞書のハッシュテーブルの実装は、キーを見つけるために、キーから計算されたハッシュ値を使っています。もしキーがミュータブルなオブジェクトだったら、その値は変えられ、それによりハッシュ値も変わってしまいます。しかし、キーオブジェクトを変更したのが何者であれ、値が辞書のキーとして使われていたと気付けないので、辞書の中のエントリを適切な場所に動かせません。そうして、同じオブジェクトを探そうとしたときに、ハッシュ値が違うため見つかりません。古い値を探そうとしても、そのハッシュバイナリから見つかるオブジェクトの値は異なるでしょうから、これも見つかりません。

リストでインデクシングされた辞書が必要なら、まず単純にリストをタプルに変換してください。関数 tuple(L) は、リスト L と同じエントリのタプルを生成します。タプルはイミュータブルなので、辞書のキーとして使えます。

いくつかの受け入れられなかった提案:

  • アドレス (オブジェクト ID) のハッシュリスト。これは、同じ値の新しいリストを作っても見つからないので駄目です。例えば:


    フレンドの紹介文を配置する方法
    mydict = {[1, 2]: '12'} print mydict[[1, 2]] 

    は、2 行目の [1, 2] の id が 1 行目のものと違うため、 KeyError 例外を起こします。要するに、辞書のキーは is ではなく、 == で比較されるべきです。

  • リストをキーとして使うときにコピーを作る。リストはミュータブルなので、自分自身への参照を含むことができ、コードをコピーするときに無限ループにハマる可能性があるので、これは駄目です。

  • リストをキーとして使うことを認めるが、ユーザにそれを変更させないように伝える。もしユーザが忘れたり、偶然にリストが変更されてしまったりしたら、追跡困難なバグの可能性を生じてしまいます。またこれは、d.keys() のすべての値は辞書のキーとして使えるという、辞書の重要な不変性も潰してしまいます。

  • リストが一旦辞書のキーとして使われたら、読み込み専用のマークを付ける。問題は、値を変えられるのはトップレベルオブジェクトだけではないことです。リストを含むタプルもキーとして使えます。全てを辞書のキーとして導入すると、そこから到達可能な全てのオブジェクトに読み込み専用のマークを付ける必要があります - そして再び、自己参照オブジェクトが無限ループを引き起こします。

必要ならばこれを回避する方法がありますが、自己責任のもとで行ってください。ミュータブルな構造を、__eq__()__hash__() メソッドの両方を持つクラスインスタンスに含めることができます。その時、辞書 (またはハッシュに基づく別の構造体) に属するような全てのラッパーオブジェクトのハッシュ値が、そのオブジェクトが辞書 (その他の構造体) 中にある間固定され続けることを確実にしてください:

class ListWrapper:     def __init__(self, the_list):         self.the_list = the_list     def __eq__(self, other):         return self.the_list == other.the_list     def __hash__(self):         l = self.the_list         result = 98767 - len(l)*555         for i, el in enumerate(l):             try:                 result = result + (hash(el) % 9999999) * 1001 + i             except Exception:                 result = (result % 7777777) + i * 333         return result 

なお、リストのメンバーの中にハッシュ化できないものがある可能性や、算術オーバーフローの可能性から、ハッシュ計算は複雑になります。

さらに、そのオブジェクトが辞書に含まれるか否かにかかわらず、o1 == o2 (すなわち o1.__eq__(o2) が真) ならばいつでも hash(o1) == hash(o2) (すなわち o1.__hash__() == o2.__hash__) でなくてはなりません。その制限に適合できなければ、辞書やその他のハッシュに基づく構造体は間違いを起こします。

この ListWrapper の例では、異常を避けるため、ラッパオブジェクトが辞書内にある限りラップされたリストが変更されてはなりません。この条件と満たせなかった時の結果について知恵を絞る覚悟がない限り、これをしてはいけません。よく考えてください。

なぜ list.sort() はソートされたリストを返さないのですか?

パフォーマンスが問題となる状況では、ソートするためだけにリストのコピーを作るのは無駄が多いです。そこで、list.sort() はインプレースにリストをソートします。このことを忘れないため、この関数はソートされたリストを返しません。こうすることで、ソートされたコピーが必要で、ソートされていないものも残しておきたいときに、うっかり上書きしてしまうようなことがなくなります。

Python 2.4 で、新しい関数 – sorted() – が追加されました。この関数は、与えられたイテレート可能から新しいリストを生成し、ソートして返します。例えば、辞書のキーをソートされた順序でイテレートする方法は:

for key in sorted(mydict):     ... # do whatever with mydict[key]... 

Python ではどのようにインタフェース仕様を特定し適用するのですか?

C++ や Java のような言語が提供するような、モジュールに対するインタフェース仕様の特定は、モジュールのメソッドや関数の原型を表現します。インタフェースの特定がコンパイル時に適用されることが、大きなプログラムの構成に役立つと、広く感じられています。

Python 2.6 で、abc モジュールが追加され、抽象基底クラス (Abstract Base Classes/ABCs) を定義できるようになりました。これにより、isinstance()issubclass() を使って、あるインスタンスやクラスが特定の ABC を実装するかを調べられるようになりました。collections モジュールによって、IterableContainerMutableMapping などの役立つ ABC が定義されています。

Python では、コンポーネントの適切なテスト規律によって、インタフェース仕様の多くの強みを活かせます。サブクラス化による問題を見つけるために使えるツール PyChecker もあります。

モジュールのための適切なテストスイートは、回帰テストを提供し、モジュールのインタフェース仕様や用例集としても役立ちます。多くの Python モジュールは、簡単な「自己テスト」を提供するスクリプトとして実行できます。複雑な外部インタフェースを使うモジュールさえ、外部インタフェースの細かい「スタブ」エミュレーションで単独にテストできることが多いです。doctestunittest モジュール、あるいはサードパーティのテストフレームワークで、モジュールのコードの全ての行に及ぶ徹底的なテストスイートを構成できます。

Python で大きくて複雑なアプリケーションを構築するとき、インタフェース仕様と同様に、適切なテスト規律も役立ちます。実際、インタフェース仕様ではテストできないプログラムの属性もあるので、それ以上にもなりえます。例えば、append() メソッドは新しい要素をある内部リストの終わりに加えます。インタフェース仕様ではこの append() の実装が実際にこれを行うかをテストできませんが、テストスイートならこの機能を簡単に確かめられます。

テストスイートを書くことはとても役に立ちますし、テストのしやすさという視点でコードを設計することにもつながります。テスト指向開発は、人気を増しつつある技法で、実際のコードを書き始める前に、最初からテストスイートの部品を書くことを求めます。もちろん、 Python で粗雑にテストケースを全く書かないこともできます。

なぜオブジェクト間でデフォルト値が共有されるのですか?

この種のバグがよく初心者プログラマに噛み付きます。この関数を考えてみてください:

def foo(mydict={}):  # Danger: shared reference to one dict for all calls     ... compute something ...     mydict[key] = value     return mydict 

初めてこの関数を呼び出した時、mydict には一つの要素があります。二回目には、foo() が実行されるときに mydict には初めから一つの要素をすでに持っているので、mydict には二つの要素があります。

関数の呼び出しによって、デフォルトの値に対する新しいオブジェクトが作られるのだと予想しがちです。実はそうなりません。デフォルト値は、関数が定義されたときに一度だけ生成されます。この例の辞書のように、そのオブジェクトが変更されたとき、その後の関数の呼び出しは変更後のオブジェクトを参照します。


定義の時に、数、文字列、タプル、None など、イミュータブルなオブジェクトを使うと変更される危険がありません。辞書、リスト、クラスインスタンスなどのミュータブルなオブジェクトは混乱のもとです。

この性質から、ミュータブルなオブジェクトをデフォルト値として使わないプログラミング手法がいいです。代わりに、None をデフォルト値に使い、そのパラメタが None である時にだけ、関数の内部で新しいリスト/辞書/その他をつくるようにしてください。例えば、こう書かずに:

こう書いてください:

def foo(mydict=None):     if mydict is None:         mydict = {}  # create a new dict for local namespace 

この性質が便利なこともあります。時間のかかる計算を行う関数があるときに使われる一般的な技法は、関数が呼び出されるごとにパラメタと結果の値をキャッシュし、再び同じ値が要求されたらキャッシュされた値を返すというものです。これは "memoizing" と呼ばれ、このように実装されます:

# Callers will never provide a third parameter for this function. def expensive (arg1, arg2, _cache={}):     if (arg1, arg2) in _cache:         return _cache[(arg1, arg2)]      # Calculate the value     result = ... expensive computation ...     _cache[(arg1, arg2)] = result           # Store result in the cache     return result

デフォルト値の代わりに、辞書を含むグローバル変数も使えます。これは好みの問題です。

なぜ goto が無いのですか?

関数の呼び出しをまたいでも動作する "構造化された goto" をまかなうものとして例外を使えます。C、Fortran、その他の言語での "go" あるいは "goto" 構造の適切な用途は全て、例外で同じようなことををすれば便利であると、広く感じられています。例えば:

class label: pass  # declare a label  try:      ...      if (condition): raise label()  # goto label      ... except label:  # where to goto      pass ... 

例外ではループ内へ跳ぶことはできませんが、どちらにしてもそれは goto の乱用と見なされるものです。使うのは控えてください。

なぜ raw 文字列 (r-strings) はバックスラッシュで終わってはいけないのですか?

正確には、奇数個のバックスラッシュで終わってはいけません。終わりの対になっていないバックスラッシュは、閉じ引用文字をエスケープし、終っていない文字列を残してしまいます。

raw 文字列は、独自にバックスラッシュの処理をしようとするプロセッサ (主に正規表現エンジン) への入力を生成しやすいように設計されたものです。このようなプロセッサは、終端の対になっていないバックスラッシュを結局エラーとみなすので、raw 文字列はそれを認めません。その代わりに、バックスラッシュでエスケープすることで、引用文字を文字列として渡すことができます。r-string が意図された目的に使われるときに、この規則が役に立つのです。

Windows のパス名を構築するときには、Windows のシステムコールは普通のスラッシュも受け付けることを憶えておいてください:

f = open("/mydir/file.txt")  # works fine! 

DOS コマンドのパス名を構築するときには、例えばこの中のどれかを試してください:

dir = r"\this\is\my\dos\dir" "\\" dir = r"\this\is\my\dos\dir\ "[:-1] dir = "\\this\\is\\my\\dos\\dir\\" 

属性の代入に "with" 文が使えないのはなぜですか?

Python には、ブロックの実行を包む 'with' 文があり、ブロックに入るときとブロックから出るときに、コードを呼び出します。以下のような構造を持つ言語があります:

with obj:     a = 1               # equivalent to obj.a = 1     total = total + 1   # obj.total = obj.total + 1 

Python では、このような構造は曖昧になるでしょう。

Object Pascal、Delphi、C++のような他の言語では、静的な型を使うので、曖昧な方法でも、どのメンバに代入されているのか分かります。これが静的型付けの要点です – コンパイラは always コンパイル時にすべての変数のスコープを知るのです。

Python は動的な型を使います。実行時にどの属性が参照されるか事前に分かりません。動作中にメンバ属性が追加あるいは除去されるかもしれません。これでは、単純に読むだけではどのアトリビュートが参照されているか分かりません。ローカルなのか、グローバルなのか、メンバ属性なのか。

例えば、以下の不完全なコード片を考えましょう:

def foo(a):     with a:         print x 

このコード片では、"a" は "x" というメンバ属性を持っていると仮定されています。しかし、Python ではインタプリタにはこの仮定を伝えられる仕組みはありません。 "a" が、例えば整数だったら、どうなってしまうでしょうか。 "x" という名前のグローバル変数があったら、それが with ブロックの中で使われるのでしょうか。この通り、Python の動的な特質から、このような選択はとても難しい物になっています。

しかし、"with" やそれに類する言語の機能の一番の利点 (コード量の削減) は、 Python では代入により簡単に手に入れられます:

function(args).mydict[index][index].a = 21 function(args).mydict[index][index].b = 42 function(args).mydict[index][index].c = 63 

こう書いてください:

ref = function(args).mydict[index][index] ref.a = 21 ref.b = 42 ref.c = 63 

Python では実行時に名前束縛が解決され、後者はその解決が一度で済むため、これには実行速度をあげる副作用もあります。

if/while/def/class 文にコロンが必要なのはなぜですか?

主に可読性を高めるため (実験的な ABC 言語の結果の一つ) に、コロンが必要です:

と:

を考えれば、後者のほうが少し読みやすいでしょう。さらに言えば、この FAQ の解答例は次のようになるでしょう。これは、英語の標準的な用法です。

他の小さな理由は、コロンによってエディタがシンタックスハイライトをしやすくなることです。手の込んだ解析をしなくても、コロンを探せばいつインデンテーションを増やすべきかを決められます。

なぜ Python ではリストやタプルの最後にカンマがあっても良いのですか?

Python では、リスト、タプル、辞書の最後の要素の後端にカンマをつけても良いことになっています:

[1, 2, 3,] ('a', 'b', 'c',) d = {     "A": [1, 5],     "B": [6, 7],  # last trailing comma is optional but good style } 

これを許すのには、いくつかの理由があります。

複数行にまたがるリスト、タプル、辞書にリテラル値を使っているとき、こうすれば前の行にカンマを加える必要がなくなるので、要素を加えやすくなります。構文エラーを引き起こすことなくエディタで行をソートできます。

間違えてカンマを落としてしまうと、診断しづらいエラーにつながります。例えば:

x = [   "fee",   "fie"   "foo",   "fum" ] 

このリストには4つの要素があるように見えますが、実際には3つしかありません。"fee、"fiefoo"、"fum" です。いつもカンマを付けるようにすれば、この種のエラーが避けられます。



These are our most popular posts:

ドイツZDF報告-原発・悪魔たちの闇のネットワーク

なぜ Python の終了時にすべてのメモリが開放されるわけではないのですか? なぜ タプルと ... この条件文が真の時のみ実行されるのは x++ 文だけですが、このインデン テーションでは誤解を招きます。経験を積んだ C ... これは様々な理由からとても便利だ と言えます。 まず、扱って ..... 定義の時に、数、文字列、タプル、None など、 イミュータブルなオブジェクトを使うと変更される危険がありません。辞書、リスト、 .... Foundation. 最終更新: learn more このドキュメントは Sphinx 1.2pre/ learn more で生成しました。 read more

危険な中古ビジネスフォン

2011年6月5日 ... この映像マジですか? : youtu.be/lhnJ1ibVMFM. ... このページの先頭です この ページの本文へ .... ドキュメント映画、ナージャの村、アレクセイの泉で見ることが出来る ) ... 皆風邪だろう年だろうっていってるけど、本当に今まで体験した事のない不調を何故 この時期にいっせいに起こしてるのか。 ... 飯舘村とか残留値高いし、死ぬほどの危険な 状況ではないですが、海水などに出された汚染水はとても危険なので、 ... read more

福島の子供で鼻血続出。この映像マジですか? - Yahoo!知恵袋

4 日前 ... このドキュメントは、原子力ムラの黒い霧が、国のエネルギー政策の中枢部分だけはで なく、私たちの日常にまで及 ... そしてその危険を理解するには、過去を理解することが 必要だ。 .... では、嘘の文書を書くよう求めたわけですか?」。 ..... 世界中であらゆる 専門家が予想していたメルトダウンの事実を、東電が認めるまでなぜ2ヶ月も要したのか 、私たちは聞こうと思った。 ... とても静かで、夏は涼しく、冬は暖かかった。 read more

デザインと歴史 FAQ — Python 2.7ja1 documentation

そんなに怖いですか? ... 内容を伴わず人づてに「オンラインブックマーク=危険」の印象 だけが強く広まり、ぼんやりとした不安感と過剰な反応を引き起こしています。 ここでは、 なぜこのような誤解が生まれたのか事の経緯を辿りながら、その誤りを指摘したいと 思います。 .... ようにするため 使用するパソコンが1つに限定されていない方(ネット カフェなど)の場合、どこからでも情報を閲覧できるオンラインブクマはとても便利なもの です。 read more

Related Posts



0 コメント:

コメントを投稿