デザインと歴史 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 の括弧がないからだけではありません -- 宣言が不要なことや高レベルなデータ型もその理由です -- が、インデントに基づく構文は確かに役に立っています。
なぜ単純な算術演算が奇妙な結果になるのですか?¶
次の質問を参照してください。
なんで浮動小数点数の計算はこんなに不正確なんですか?¶
ユーザーはよく次のような結果に驚きます:
>>> 1.2 - 1.0
0.19999999999999996
そしてこれが Python のバグだと考えます。が、これはバグではありません。この結果に Python はほとんど関与しておらず、むしろ基底のプラットフォームによる浮動小数点数の扱い方が関与しています。
CPython における float
型は記憶に C 言語の double
型を使います。 float
オブジェクトの値は固定精度 (典型的には 53 bit) の 2 進浮動小数点数として格納され、 Python はプロセッサのハードウェアが実装している C 言語上の演算を使います。つまり、浮動小数点数に関して Python は C 言語や Java のような多くの一般的な言語と同じように振る舞います。
10 進数で簡単に書ける多くの数が、2 進浮動小数点数では正確に表すことができません。例えばこうすると:
>>> x = 1.2
x
に保存された値は 10 進数の 1.2
の (とても高い精度の) 近似値であって、厳密な 1.2
ではありません。一般的なコンピューターでは、実際に格納される値は:
1.0011001100110011001100110011001100110011001100110011 (binary)
で、正確には次の値です:
1.1999999999999999555910790149937383830547332763671875 (decimal)
53bit の典型的な精度は、Python の float に 10 進数で 15〜16桁の精度を与えます。
詳しくは、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(", ")
これは文字列リテラルに対する、与えられたセパレータ (または、デフォルトでは任意の空白文字の連続) で区切られた部分文字列を返せという指示です。
join()
は、セパレータ文字列に、文字列のシーケンスをイテレートして隣り合う要素の間に自身を挿入するように指示しているので、文字列のメソッドです。このメソッドは、独自に定義された新しいクラスを含め、シーケンスの規則を満たすいかなる引数にも使えます。バイト列やバイト配列にも同様のメソッドがあります。
例外はどれくらい速いのですか?¶
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)
この特殊な場合では value = dict.setdefault(key, getvalue(key))
も使えますが、これは getvalue()
呼び出しが十分安価な場合に限ります。なぜならそれが全ての場合に評価されるからです。
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 実装 CPython は参照カウントを使って、アクセスできないオブジェクトを探します。また別のメカニズムを使って参照サイクルを集めます。これはサイクル検出アルゴリズムを定期的に実行し、アクセスできないサイクルを探し、巻き込まれたオブジェクトを削除します。 gc
モジュールの関数で、ガベージコレクションを実行し、デバッグ統計を取得し、コレクタのパラメタを変更できます。
ただし、他の実装 (例えば Jython や PyPy) は本格的 (full-blown) なガベージコレクタのような別のメカニズムに依存するかもしれません。あなたの Python コードが参照カウントの実装の振る舞いに依存する場合、この違いが微妙な移植上の問題を引き起こすでしょう。
Python の実装によっては、以下の (CPython では通る) コードはおそらく、ファイルディスクリプタを使い果たすでしょう:
for file in very_long_list_of_files:
f = open(file)
c = f.read(1)
実際、CPython の参照カウントとデストラクタのスキームを使えば f への新しい代入ごとにファイルは閉じられます。しかし、伝統的な GC を使うと、これらのファイルオブジェクトが回収され (て閉じられる) までに不定な、場合によっては長い、間隔が空くことがあります。
Python の実装に依らずに動くコードを書くには、ファイルを明示的に閉じるか、 with
文を使ってください。これでメモリ管理のスキームに関係なく動きます:
for file in very_long_list_of_files:
with open(file) as f:
c = f.read(1)
CPython はなぜ伝統的なガベージコレクションスキームを使わないのですか?¶
まず、それは C の標準的な機能ではないのでポータブルではありません。(確かに Boehm GC ライブラリはあります。しかし、これにはアセンブリコードが含まれ、ほとんどの 有名なプラットフォームに対応していますが全てではありません。また、ほとんど透過的ですが、完全に透過的ではありません。Python を対応させるにはパッチが必要です。)
伝統的な GC は Python が他のアプリケーションに組み込まれるときにも問題となります。スタンドアロンの Python で動く限りでは、標準の malloc() と free() を GC ライブラリから提供されるものに置き換えても問題ありませんが、Python を実装したアプリケーションは Python のものではない 独自の 代替品を使おうとするかもしれません。現在のようにすることで、CPython は malloc() と free() が適切に実装されている限りどんなものにも対応させられます。
なぜ CPython の終了時にすべてのメモリが解放されるわけではないのですか?¶
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()
はインプレースにリストをソートします。このことを忘れないため、この関数はソートされたリストを返しません。こうすることで、ソートされたコピーが必要で、ソートされていないものも残しておきたいときに、うっかり上書きしてしまうようなことがなくなります。
新しいリストを返したいなら、代わりに組み込みの sorted()
関数を使ってください。この関数は、与えられたイテレート可能オブジェクトから新しいリストを生成し、ソートして返します。例えば、辞書のキーをソートされた順序でイテレートする方法は:
for key in sorted(mydict):
... # do whatever with mydict[key]...
Python ではどのようにインタフェース仕様を特定し適用するのですか?¶
C++ や Java のような言語が提供するような、モジュールに対するインタフェース仕様の特定は、モジュールのメソッドや関数の原型を表現します。インタフェースの特定がコンパイル時に適用されることが、大きなプログラムの構成に役立つと、広く感じられています。
Python 2.6 で、抽象基底クラス (Abstract Base Class, ABC) が定義できるようになる abc
モジュールが追加されました。なので isinstance()
と issubclass()
を使って、インスタンスやクラスが、ある ABC を実装しているかどうかチェックできます。collections.abc
モジュールでは、 Iterable
、 Container
、 MutableMapping
などの便利な ABC が定義されています。
Python では、コンポーネントの適切なテスト規律によって、インタフェース仕様の多くの強みを活かせます。サブクラス化による問題を見つけるために使えるツール PyChecker もあります。
モジュールのための適切なテストスイートは、回帰テストを提供し、モジュールのインタフェース仕様や用例集としても役立ちます。多くの Python モジュールは、簡単な「自己テスト」を提供するスクリプトとして実行できます。複雑な外部インタフェースを使うモジュールさえ、外部インタフェースの細かい「スタブ」エミュレーションで単独にテストできることが多いです。 doctest
や unittest
モジュール、あるいはサードパーティのテストフレームワークで、モジュールのコードの全ての行に及ぶ徹底的なテストスイートを構成できます。
Python で大きくて複雑なアプリケーションを構築するとき、インタフェース仕様と同様に、適切なテスト規律も役立ちます。実際には、インタフェース仕様ではテストできないプログラムの属性もあるので、それ以上にもなりえます。例えば、 append()
メソッドは新しい要素をある内部リストの終わりに加えます。インタフェース仕様ではこの append()
の実装が実際にこれを行うかをテストできませんが、テストスイートならこの機能を簡単に確かめられます。
テストスイートを書くことはとても役に立ちますし、テストのしやすさという視点でコードを設計することにもつながります。テスト指向開発は、人気を増しつつある技法で、実際のコードを書き始める前に、最初からテストスイートの部品を書くことを求めます。もちろん、 Python で粗雑にテストケースを全く書かないこともできます。
なぜ goto が無いのですか?¶
関数の呼び出しをまたいでも動作する "構造化された goto" をまかなうものとして例外を使えます。C、Fortran、その他の言語での "go" あるいは "goto" 構造の適切な用途は全て、例外で同じようなことををすれば便利であると、広く感じられています。例えば:
class label(Exception): 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" です。 常にカンマを付けるようにすれば、この種のエラーが避けられます。
後端にカンマをつけても良いことにすれば、プログラムによるコード生成も簡単になります。