デザインと歴史 FAQ¶
Python はなぜ文のグループ化にインデントを使うのですか?¶
Guido van Rossum の信じるところによれば、インデントによるグループ化は非常にエレガントで、普通の Python プログラムを大いに読みやすくします。しばらくすればほとんどの人はこの仕様を気に入るようになります。
開始/終了の括弧がないので、構文解析器と人間の読者の間にグループ化の解釈の違いは起こりえません。時折、C のプログラマはこのようなコード片に出くわします:
if (x <= y)
x++;
y--;
z++;
条件式が真のとき、 x++
行のみが実行されます。しかしインデントによって、多くの人が別のことを考えてしまいます。経験豊富なCプログラマでさえ、 x > y
なのに 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 のような多くの一般的な言語と同じように振る舞います。
Many numbers that can be written easily in decimal notation cannot be expressed exactly in binary floating point. For example, after:
>>> x = 1.2
x
に保存された値は 10 進数の 1.2
の (とても高い精度の) 近似値であって、厳密な 1.2
ではありません。一般的なコンピューターでは、実際に格納される値は:
1.0011001100110011001100110011001100110011001100110011 (binary)
で、正確には次の値です:
1.1999999999999999555910790149937383830547332763671875 (decimal)
53bit の典型的な精度は、Python の float に 10 進数で 15〜16桁の精度を与えます。
For a fuller explanation, please see the floating-point arithmetic chapter in the Python tutorial.
なぜ 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 にどちらの名前空間を使うかを伝えなくてはならないのです。
式中で代入ができないのはなぜですか?¶
Python 3.8 以降ならできるよ!
セイウチ演算子 :=
を使った代入式は、式の中で変数に代入します:
while chunk := fp.read(200):
print(chunk)
より詳しくは PEP 572 を参照してください。
Python にメソッドを使う機能 (list.index() 等) と関数を使う機能 (len(list) 等) があるのはなぜですか?¶
Guidoいわく:
(a) 幾つかの演算では、接頭辞は接尾辞よりも単純に読みやすいからです。接頭辞(そして接中辞!)による演算は数学において長い歴史があり、そこでは課題に対する数学者の思考を視覚的に助けるような記法が好まれます。x*(a+b)をx*a + x*bに書き換える容易さと、それと同じことを純粋なオブジェクト指向の記法で行う煩わしさを比較してみてください。
(b) len(x)というコードを読んだ時、私はそれが何かの長さを問うているのだなと知ることができます。これは私に2つの事を知らせています。一つは結果が整数であること、そして引数は何らかのコンテナであることです。対して、x.len()を目にした場合、私はその時点でxが何らかのコンテナであり、それが標準のlen()を持っているクラスを継承しているか、インターフェースを実装していることを知っている必要があります。mappingを実装していないクラスがget()やkeys()メソッドを持っていたり、fileでない何かがwrite()メソッドを持っているような混乱は時折見かけます。
—https://mail.python.org/pipermail/python-3000/2006-November/004643.html
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 文がないのはなぜですか?¶
一般的に、構造化された switch 文は、式が特定の値または値の集合を持つとき、1つのコードブロックを実行します。 Python 3.10 以降では、リテラル値や名前空間内の定数を、 match ... case
文で簡単にマッチさせることができます。より古い手段は一連の if... elif... elif... else
です。
非常に大きな数の選択肢から選ぶとき、値を呼び出す関数に対応づける辞書を作れます。例えば:
functions = {'a': function_1,
'b': function_2,
'c': self.method_1}
func = functions[value]
func()
オブジェクトのメソッドを呼び出すには、さらに単純に getattr()
組み込み関数で特定の名前のメソッドを検索できます:
class MyVisitor:
def visit_a(self):
...
def dispatch(self, value):
method_name = 'visit_' + str(value)
method = getattr(self, method_name)
method()
メソッドの名前にこの例の visit_
のような接頭辞を使うことを勧めます。このような接頭辞がないと、信頼できないソースから値が与えられたときに、オブジェクトの任意のメソッドを呼び出す攻撃をされる可能性があります。
C の switch-case-default のような、フォールスルーのある switch を模倣することもできますが、はるかに難しいうえに必要性も少ないでしょう。
OS 特有のスレッド実装に依らずにインタプリタでスレッドをエミュレートすることはできないのですか?¶
答 1: 残念なことに、インタプリタは Python のスタックフレームごとに少なくとも一つの C のスタックフレームを push します。同様に、拡張もほとんどランダムなときに Python にコールバックすることがあります。よって、完全なスレッド実装には C のスレッドサポートが必要です。
答 2: 幸運なことに、Stackless Python があります。これは完全に再デザインされたインタープリタで、Cのスタックを回避しています。
なぜラムダ式は文を含むことができないのですか?¶
Python のラムダ式が文を含むことができないのは、Python の文法的な枠組みが式の中にネストされた文を扱うことができないからです。しかし、Python では、これは深刻な問題ではありません。他の言語のラムダに機能が追加されているのと違い、Python のラムダは単なる、関数を定義するのが面倒すぎる場合のための簡略な記法に過ぎないのです。
関数は既に Python の第一級オブジェクトで、ローカルスコープ内で宣言できます。従って、ローカルで定義された関数ではなくラムダを使う利点は、関数の名前を考える必要が無いことだけです -- しかし、(ラムダ式が生み出すオブジェクトと厳密に同じ型の) 関数オブジェクトが代入される先はただのローカル変数です!
Python は C やその他の言語のように機械語にコンパイルできますか?¶
Cython compiles a modified version of Python with optional annotations into C extensions. Nuitka is an up-and-coming compiler of Python into C++ code, aiming to support the full Python language.
Python はメモリをどのように管理するのですか?¶
Python のメモリ管理の詳細は実装に依ります。Python の標準の C 実装 CPython は参照カウントを使って、アクセスできないオブジェクトを探します。また別のメカニズムを使って参照サイクルを集めます。これはサイクル検出アルゴリズムを定期的に実行し、アクセスできないサイクルを探し、巻き込まれたオブジェクトを削除します。 gc
モジュールの関数で、ガベージコレクションを実行し、デバッグ統計を取得し、コレクタのパラメタを変更できます。
Other implementations (such as Jython or PyPy), however, can rely on a different mechanism such as a full-blown garbage collector. This difference can cause some subtle porting problems if your Python code depends on the behavior of the reference counting implementation.
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('.')
はカレントディレクトリ内にあるファイルの文字列表現のリストを返します。この出力を演算する関数は一般に、ディレクトリに一つや二つの別のファイルを加えても壊れません。
タプルはイミュータブルなので、一度タプルが生成されたら、そのどの要素も新しい値に置き換えられません。リストはミュータブルなので、リストの要素はいつでも変更できます。イミュータブルな要素だけが辞書のキーとして使えるので、リストではなくタプルだけがキーとして使えます。
CPythonでリストはどのように実装されているのですか?¶
CPythonのリストは実際に変数分の長さの配列で、Lispスタイルの連結リストではありません。この実装は他のオブジェクトへの参照の連続した配列を使用していて、この配列へのポインタおよび配列長はリストの先頭の構造体に保存されています。
これにより、リストのインデクシング a[i]
は、リストの大きさやインデクスの値に依存しないコストで演算できます。
要素が追加または挿入されるとき、この参照の配列は大きさが変更されます。要素追加の繰り返しのパフォーマンスを上げるために、少し工夫されています。配列が大きくなるとき、次の何回かは実際に大きさを変更する必要がないように、いくらかの追加の領域が割り当てられます。
CPythonで辞書はどのように実装されていますか?¶
CPython の辞書は大きさを変更できるハッシュテーブルとして実装されています。B 木と比べて、ほとんどの条件下で (特に一般的な演算である) 探索のパフォーマンスが良いですし、実装も単純です。
辞書は、辞書に保存されているそれぞれのキーに対応するハッシュコードを hash()
ビルトイン関数で計算することで機能します。このハッシュコードはキーやプロセスごとのシードによって大きく変化します。例えば、 'Python'
のハッシュ値は -539294296
ですが、ビットが一つ違うだけの文字列 'python'
のハッシュ値は 1142331976
です。そしてこのハッシュコードは、値が保存される内部配列での位置を計算するために使われます。保存しているキーのハッシュ値が全て異なるとすれば、一定の時間 − Big-O 記法では O(1) − でキーを検索できることになります。
なぜ辞書のキーはイミュータブルでなくてはならないのですか?¶
辞書のハッシュテーブルの実装は、キーを見つけるために、キー値から計算されたハッシュ値を使います。もしキーがミュータブルなオブジェクトだったら、その値は変えられ、それによりハッシュ値も変わってしまいます。しかし、キーオブジェクトを変更したのが何者であれ、値が辞書のキーとして使われていたと気付けないので、辞書の中のエントリを適切な場所に動かせません。そして、同じオブジェクトを探そうとしても、ハッシュ値が違うため見つかりません。古い値を探そうとしても、そのハッシュバイナリから見つかるオブジェクトの値は異なるでしょうから、これも見つかりません。
リストでインデクシングされた辞書が必要なら、まず単純にリストをタプルに変換してください。関数 tuple(L)
は、リスト L
と同じエントリのタプルを生成します。タプルはイミュータブルなので、辞書のキーとして使えます。
いくつかの受け入れられなかった提案:
アドレス (オブジェクト ID) のハッシュリスト。これは、同じ値の新しいリストを作っても見つからないので駄目です。例えば:
mydict = {[1, 2]: '12'} print(mydict[[1, 2]])
[1, 2]
の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では、インターフェース仕様の多くの利点は、コンポーネントへの適切なテスト規律により得られます。
モジュールのための適切なテストスイートは、回帰テストを提供し、モジュールのインターフェース仕様や用例集としても役立ちます。多くの Python モジュールは、簡単な「自己テスト」を提供するスクリプトとして実行できます。複雑な外部インターフェースを使うモジュールさえ、外部インターフェースの細かい「スタブ」エミュレーションで単独にテストできることが多いです。 doctest
や unittest
モジュール、あるいはサードパーティのテストフレームワークで、モジュールのコードの全ての行に及ぶ徹底的なテストスイートを構成できます。
Python で大きくて複雑なアプリケーションを構築するとき、インターフェース仕様と同様に、適切なテスト規律も役立ちます。実際には、インターフェース仕様ではテストできないプログラムの属性もあるので、それ以上にもなりえます。例えば、 list.append()
メソッドは新しい要素をある内部リストの終わりに加えます。インターフェース仕様ではこの list.append()
の実装が実際にこれを行うかをテストできませんが、テストスイートならこの機能を簡単に確かめられます。
テストスイートを書くことはとても有用ですし、簡単にテストできるコード設計を心がけると良いでしょう。人気を博している開発手法の一つ、テスト駆動開発は、実際のコードを記述するよりも先に、まずテストスイートの部分を記述するよう求めています。ご心配なく、Python は、あなたがいい加減でもテストケースを全く書かなくても構いません。
なぜ goto が無いのですか?¶
1970年代、人々は気付きました。秩序なき goto は、理解するのも手直しするのも困難という厄介な"スパゲッティ"コードに陥りがちであると。
高水準言語では、分岐とループの手段があれば goto は不要です。
(Pythonだと、分岐には if
文及び or
・ and
・ if
/else
式を使います。ループには while
文と for
文を使い、 ループ内に continue
・ break
を含むことがあります)
関数の呼び出しをまたいでも動作する "構造化された 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 では実行時に名前束縛が解決され、後者はその解決が一度で済むため、これには実行速度をあげる副作用もあります。
似た提案として、「先頭のドット」を使うなどして さらにコード量を減らす構文は、明白さを優先をして却下されました (https://mail.python.org/pipermail/python-ideas/2016-May/040070.html 参照)。
なぜジェネレータは with 文をサポートしないのですか?¶
技術的な理由で、ジェネレータは直接コンテキストマネージャとして使ってもうまく動きません。最も一般的なように、ジェネレータが最後まで回りきるイテレータとして使われる場合、クローズ処理は不要です。必要な場合は、 with
文で contextlib.closing(generator)
のようにラップします。
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" です。 常にカンマを付けるようにすれば、この種のエラーが避けられます。
後端にカンマをつけても良いことにすれば、プログラムによるコード生成も簡単になります。