デザインと歴史 FAQ¶
Python はなぜ文のグループ化にインデントを使うのですか?¶
Guido van Rossum の信じるところによれば、インデントによるグループ化は非常にエレガントで、平均的な Python プログラムを大いに読みやすくします。しばらくすればほとんどの人はこの特徴を気に入るようになります。
開始/終了の括弧がないので、構文解析器と人間の読者の間にグループ化の解釈の違いは起こりえません。時折、C のプログラマはこのようなコード片に出くわします:
if (x <= y)
x++;
y--;
z++;
この条件文が真の時のみ実行されるのは x++
文だけですが、このインデントでは誤解を招きます。経験を積んだ C プログラマでさえ、 y
が x > y
の時にもデクリメントされるのはなぜか分からず長いこと立ち止まることがあるでしょう。
開始/終了の括弧がないので、Python はコーディングスタイルの対立が非常に起こりにくくなります。C では多様なカッコの置き方があります。一つのスタイルでのコードの読み書きに慣れてしまうと、他のスタイルを読む (あるいは書く必要がある) ときにむずむずするでしょう。
多くのコーディングスタイルは begin/end の括弧にそれぞれ一行を使います。これではプログラムは冗長になって画面を浪費し、プログラムの見通しが悪くなります。一つの関数は一画面 (例えば 20 から 30 行) に収めるのが理想です。20 行の Python は 20 行の C よりもはるかに多くの作業ができます。これは begin/end の括弧がないからだけではありません – 宣言が不要なことや高レベルなデータ型もその理由です – が、インデントに基づく構文は確かに役に立っています。
なぜ単純な算術演算が奇妙な結果になるのですか?¶
次の質問を参照してください。
なぜ浮動小数点演算はこれほど不正確なのですか?¶
このような結果は、よく驚かれたり Python のバグであると考えられたりします:
>>> 1.2 - 1.0
0.19999999999999996
でもこれはバグではありません。これは 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.x
や self.meth()
と書いてあれば、そのクラスの定義を憶えていなくても、それがインスタンス変数やメソッドであることは明らかです。C++ では、(グローバルが滅多になかったり、簡単に見分けがつくなら) ローカル変数宣言がないことからある程度わかるでしょう。– しかし Python にはローカル変数宣言がないので、クラス定義を調べて確かめなくてはなりません。C++ や Java のコーディングスタンダードに、インスタンス属性に m_
接頭辞をつけるものがあるので、この明示性はそれらの言語においても有用です。
第二に、特定のクラスからメソッドを明示的に参照または呼び出ししたい時に、特別な構文が必要なくなります。C++ では、派生クラスでオーバーライドされた基底クラスからメソッドを使うには、 ::
演算子を使わなければなりません。 – Python では、 baseclass.methodname(self, <argument 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() 等) と関数を使う機能 (len(list) 等) があるのはなぜですか?¶
主な理由は歴史です。複数の型に対しての総称的な操作で、対象のオブジェクトがメソッドを全く持っていなかった (例えば、タプル) としても働くよう意図したものに関数は使われました。Python の関数的機能 (map()
、zip()
など) を使うときに、型のはっきりしないオブジェクトのコレクションに対して、難なく適用できる関数があるのも便利なことです。
実際、 len()
、 max()
、 min()
を組み込み関数として実装することで、それぞれの型のメソッドとして実装するより少ないコードで済みます。個々のケースについては粗探しのしようがありますが、Python の一部であるし、根本的な変更をするには遅すぎます。これらの関数は、大規模なコードの破壊を避けるために残す必要があります。
注釈
Python の文字列演算は、外部の関数 (string
モジュール) からメソッドに移行しました。しかし、 len()
は関数のままです。
join() がリストやタプルのメソッドではなく文字列のメソッドなのはなぜですか?¶
文字列は Python 1.6 から他の標準型に大きく近づきました。それ以前は常に string モジュールの関数を使ってできていたことと同等の機能を持つメソッドがこの時に追加されました。その新しいメソッドの多くは広く受け入れられましたが、一部のプログラマに不快を感じさせていると思われるものがこれで:
", ".join(['1', '2', '4', '8', '16'])
結果はこうなります:
"1, 2, 4, 8, 16"
この使い方には二つの議論があります。
一つ目は、「文字列リテラル (文字列定数) のメソッドを使うのは醜すぎる」というようなものです。確かにそうかも知れませんが、文字列リテラルは単なる固定された値に過ぎないというのが答えです。文字列に束縛された名前にメソッドが許されるなら、リテラルに使えないようにする論理的な理由はないでしょう。
二つ目の反対理由は、典型的には「私は実際、要素を文字列定数とともに結合させるよう、シーケンスに命じているのだ」というものです。残念ながら、そうではないのです。いくつかの理由から split()
を文字列のメソッドとしておいた方がはるかに簡単です。これを見ると分かりやすいでしょう
"1, 2, 4, 8, 16".split(", ")
これは文字列リテラルに与えられた分離子 (デフォルトでは空白文字) によって区切られた部分文字列を返すように指示しています。このとき、Unicode 文字列は Unicode 文字列のリストを返し、ASCII 文字列は ASCII 文字列のリストを返すから、みんな幸せです。
join()
は、セパレータ文字列に、文字列のシーケンスをイテレートして隣り合う要素の間に自身を挿入するように指示しているので、文字列のメソッドです。このメソッドは、独自に定義された新しいクラスを含め、シーケンスの規則を満たすいかなる引数にも使えます。
これは文字列メソッドなので、Unicode 文字列にも通常の ASCII 文字列にも使えます。 join()
がシーケンス型のモジュールだったとしたら、そのシーケンス型はどちらの型の文字列を返すか、セパレータの型によって決めなければなりません。
もしもこれらの論拠のどれにも納得出来ないなら、さしあたり string モジュールの join()
関数を使い続けてこのように書けるでしょう
string.join(['1', '2', '4', '8', '16'], ", ")
例外はどれくらい速いのですか?¶
try/except ブロックは例外が送出されなければ極端に効率的です。実際に例外を捕捉するのは高価です。Python 2.0 より前のバージョンでは、このイディオムを使うのが一般的でした:
try:
value = mydict[key]
except KeyError:
mydict[key] = getvalue(key)
value = mydict[key]
これは、辞書がほとんどの場合にキーを持っていると予想できるときにのみ意味をなします。そうでなければ、このように書きます:
if key in mydict:
value = mydict[key]
else:
value = mydict[key] = getvalue(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 は C やその他の言語のように機械語にコンパイルできますか?¶
Cython は オプションのアノテーション付きのPythonの修正版を C拡張へ変換します。Nuitka はPythonをC++コードへ変換する将来有望なPythonコンパイラで、完全なPython言語をサポートすることを目的としています。Javaへコンパイラするには、 VOC を検討してください。
Python はメモリをどのように管理するのですか?¶
Python のメモリ管理の詳細は実装に依ります。Python の標準の C 実装は参照カウントを使って、アクセスできないオブジェクトを探します。また別のメカニズムも使って参照サイクルを集めます。これはサイクル検出アルゴリズムを定期的に実行し、アクセスできないサイクルを探し、それに含まれるオブジェクトを削除します。 gc
モジュールの関数で、ガベージコレクションを実行し、デバッグ統計を取得し、コレクタのパラメタを変更できます。
Jython は Java ランタイムに頼るので、JVM のガベージコレクタが使われます。もしもあなたの Python のコードが参照カウントの実装の振る舞いに依存しているならば、この違いが微妙な移植問題を起こすことがあります。
ときどきオブジェクトは一時的にトレースバックに貼り付き、それゆえにあなたが期待するようには解放されません。トレースバックをクリアするにはこうします:
import sys
sys.exc_clear()
sys.exc_traceback = sys.last_traceback = None
トレースバックはエラー報告、デバッガの実装やその他関係することに使います。 それらは、例外 (普通は最も最近の例外)のハンドリングで抽出されたプログラムの実行状態の一部を含んでいます。
循環性とトレースバックがなければ、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('.')
はカレントディレクトリ内にあるファイルの文字列表現のリストを返します。この出力を演算する関数は一般に、ディレクトリに一つや二つの別のファイルを加えても壊れません。
タプルはイミュータブルなので、一度タプルが生成されたら、そのどの要素も新しい値に置き換えられません。リストはミュータブルなので、リストの要素はいつでも変更できます。イミュータブルな要素だけが辞書のキーとして使えるので、リストではなくタプルだけがキーとして使えます。
How are lists implemented?¶
Python’s lists are really variable-length arrays, not Lisp-style linked lists. The implementation uses a contiguous array of references to other objects, and keeps a pointer to this array and the array’s length in a list head structure.
これにより、リストのインデクシング a[i]
は、リストの大きさやインデクスの値に依存しないコストで演算できます。
要素が追加または挿入されるとき、この参照の配列は大きさが変更されます。要素追加の繰り返しのパフォーマンスを上げるために、少し工夫されています。配列が大きくなるとき、次の何回かは実際に大きさを変更する必要がないように、いくらかの追加の領域が割り当てられます。
How are dictionaries implemented?¶
Python’s dictionaries are implemented as resizable hash tables. Compared to B-trees, this gives better performance for lookup (the most common operation by far) under most circumstances, and the implementation is simpler.
辞書は、 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) is True
) ならばいつでも 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
モジュールによって、 Iterable
、 Container
、 MutableMapping
などの役立つ ABC が定義されています。
Python では、コンポーネントの適切なテスト規律によって、インタフェース仕様の多くの強みを活かせます。サブクラス化による問題を見つけるために使えるツール PyChecker もあります。
モジュールのための適切なテストスイートは、回帰テストを提供し、モジュールのインタフェース仕様や用例集としても役立ちます。多くの Python モジュールは、簡単な「自己テスト」を提供するスクリプトとして実行できます。複雑な外部インタフェースを使うモジュールさえ、外部インタフェースの細かい「スタブ」エミュレーションで単独にテストできることが多いです。 doctest
や unittest
モジュール、あるいはサードパーティのテストフレームワークで、モジュールのコードの全ての行に及ぶ徹底的なテストスイートを構成できます。
Python で大きくて複雑なアプリケーションを構築するとき、インタフェース仕様と同様に、適切なテスト規律も役立ちます。実際には、インタフェース仕様ではテストできないプログラムの属性もあるので、それ以上にもなりえます。例えば、 append()
メソッドは新しい要素をある内部リストの終わりに加えます。インタフェース仕様ではこの append()
の実装が実際にこれを行うかをテストできませんが、テストスイートならこの機能を簡単に確かめられます。
テストスイートを書くことはとても役に立ちますし、テストのしやすさという視点でコードを設計することにもつながります。テスト指向開発は、人気を増しつつある技法で、実際のコードを書き始める前に、最初からテストスイートの部品を書くことを求めます。もちろん、 Python で粗雑にテストケースを全く書かないこともできます。
なぜ 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++のような他の言語では、静的な型を使うので、曖昧な方法でも、どのメンバに代入されているのか分かります。これが静的型付けの要点です – コンパイラは いつでも コンパイル時にすべての変数のスコープを知るのです。
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 言語の結果の一つ) に、コロンが必要です:
if a == b
print a
と:
if a == b:
print a
を考えれば、後者のほうが少し読みやすいでしょう。さらに言えば、この 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" です。いつもカンマを付けるようにすれば、この種のエラーが避けられます。
後端にカンマをつけても良いことにすれば、プログラムによるコード生成も簡単になります。