デザインと歴史に関するFAQ—Pythonドキュメント

提供:Dev Guides
< PythonPython/docs/2.7/faq/design
移動先:案内検索

デザインと歴史に関するFAQ

コンテンツ


Pythonがステートメントのグループ化にインデントを使用するのはなぜですか?

Guido van Rossumは、グループ化にインデントを使用することは非常にエレガントであり、平均的なPythonプログラムの明確さに大きく貢献すると考えています。 ほとんどの人はしばらくするとこの機能を愛するようになります。

開始/終了ブラケットがないため、パーサーと人間のリーダーによって認識されるグループ化の間に不一致が生じることはありません。 時折、Cプログラマーは次のようなコードの断片に遭遇するでしょう。

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

条件が真の場合はx++ステートメントのみが実行されますが、インデントにより、そうでない場合は信じられます。 経験豊富なCプログラマーでさえ、x > yでもyがデクリメントされているのはなぜかと長い間見つめていることがあります。

開始/終了ブラケットがないため、Pythonはコーディングスタイルの競合を起こしにくいです。 Cでは、中括弧を配置するさまざまな方法があります。 あるスタイルを使用するコードの読み取りと書き込みに慣れている場合、別のスタイルを読み取る(または書き込む必要がある)ときに、少なくとも少し不安を感じるでしょう。

多くのコーディングスタイルは、それ自体で行に開始/終了ブラケットを配置します。 これにより、プログラムがかなり長くなり、貴重な画面スペースが無駄になり、プログラムの概要を把握するのが難しくなります。 理想的には、関数は1つの画面(たとえば、20〜30行)に収まる必要があります。 20行のPythonは、20行のCよりもはるかに多くの作業を実行できます。 これは、開始/終了ブラケットの欠如(宣言の欠如と高レベルのデータ型も原因)だけが原因ではありませんが、インデントベースの構文は確かに役立ちます。


単純な算術演算で奇妙な結果が得られるのはなぜですか?

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


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

人々はしばしばこのような結果に非常に驚いています:

>>> 1.2 - 1.0
0.19999999999999996

Pythonのバグだと思います。 そうではありません。 これはPythonとは何の関係もありませんが、基盤となるCプラットフォームが浮動小数点数を処理する方法と、最終的には固定桁数の文字列として数値を書き留めるときに導入される不正確さと関係があります。

浮動小数点数の内部表現では、固定数の2進数を使用して10進数を表します。 一部の10進数は、2進数で正確に表すことができないため、小さな丸め誤差が発生します。

10進数の数学では、固定の10進数で表すことができない多くの数値があります。 1/3 = 0.3333333333……。

基数2では、1/2 = 0.1、1 / 4 = 0.01、1 / 8 = 0.001など。 .2は2/10が1/5に等しいため、2進数の小数は0.001100110011001になります…

浮動小数点数の精度は32ビットまたは64ビットしかないため、ある時点で桁が切り捨てられ、結果の数値は0.2ではなく10進数で0.199999999999999996になります。

浮動小数点数のrepr()関数は、eval(repr(f)) == fを任意のfloatfに対して真にするために必要な桁数を出力します。 str()関数はより少ない桁数を出力し、これにより、おそらく意図されたより賢明な数値が得られることがよくあります。

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

この結果の1つは、ある計算の結果を==のfloatと比較するとエラーが発生しやすいことです。 わずかな不正確さは、==が失敗することを意味する場合があります。 代わりに、2つの数値の差が特定のしきい値未満であることを確認する必要があります。

epsilon = 0.0000000000001  # Tiny allowed error
expected_result = 0.4

if expected_result-epsilon <= computation() <= expected_result+epsilon:
    ...

詳細については、Pythonチュートリアルの浮動小数点演算の章を参照してください。


Python文字列が不変なのはなぜですか?

いくつかの利点があります。

1つはパフォーマンスです。文字列が不変であることがわかっているということは、作成時に文字列にスペースを割り当てることができ、ストレージ要件は固定されており、変更されないことを意味します。 これは、タプルとリストを区別する理由の1つでもあります。

もう1つの利点は、Pythonの文字列が数値として「要素」と見なされることです。 アクティビティの量によって値8が他の値に変更されることはなく、Pythonでは、アクティビティの量によって文字列「8」が他の値に変更されることはありません。


メソッド定義と呼び出しで「self」を明示的に使用する必要があるのはなぜですか?

アイデアはModula-3から借りました。 さまざまな理由から、非常に便利であることがわかりました。

まず、ローカル変数の代わりにメソッドまたはインスタンス属性を使用していることがより明白です。 self.xまたはself.meth()を読むと、クラス定義を心から知らなくても、インスタンス変数またはメソッドが使用されていることが完全に明らかになります。 C ++では、ローカル変数宣言がないことでわかります(グローバルはまれであるか、簡単に認識できると想定しています)が、Pythonではローカル変数宣言がないため、クラス定義を検索する必要があります。もちろん。 一部のC ++およびJavaコーディング標準では、インスタンス属性にm_プレフィックスを付ける必要があるため、この明示性はこれらの言語でも役立ちます。

次に、特定のクラスからメソッドを明示的に参照または呼び出す場合は、特別な構文は必要ありません。 C ++では、派生クラスでオーバーライドされる基本クラスのメソッドを使用する場合は、::演算子を使用する必要があります。Pythonではbaseclass.methodname(self, <argument list>)と記述できます。 これは、__init__()メソッドの場合に特に役立ちます。一般に、派生クラスメソッドが同じ名前の基本クラスメソッドを拡張する必要があるため、何らかの方法で基本クラスメソッドを呼び出す必要がある場合に役立ちます。

最後に、インスタンス変数の場合、代入に関する構文上の問題を解決します。Pythonのローカル変数は(定義上!)関数本体で値が割り当てられる(そしてグローバルとして明示的に宣言されていない)変数であるため、次のことを行う必要があります。割り当てがローカル変数ではなくインスタンス変数に割り当てることを意図していることをインタプリタに伝えるための何らかの方法であり、(効率上の理由から)構文的であることが望ましい。 C ++は宣言を介してこれを行いますが、Pythonには宣言がないため、この目的のためだけに宣言を導入しなければならないのは残念です。 明示的なself.varを使用すると、これをうまく解決できます。 同様に、インスタンス変数を使用する場合、self.varを記述する必要があるということは、メソッド内の非修飾名への参照がインスタンスのディレクトリを検索する必要がないことを意味します。 言い換えると、ローカル変数とインスタンス変数は2つの異なる名前空間に存在し、使用する名前空間を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イディオムを認識しており、式コンストラクトの割り当てをあまり見逃していないように見えることです。 これを言語に追加したいという強い願望を表明するのは新参者だけです。

これを綴る別の方法がありますが、これは魅力的ですが、一般的に「whileTrue」ソリューションよりも堅牢ではありません。

line = f.readline()
while line:
    ...  # do something with line...
    line = f.readline()

これに伴う問題は、次の行を正確に取得する方法について考えを変えた場合(たとえば、 sys.stdin.readline())に変更する場合は、プログラムの2つの場所を変更することを忘れないでください。2番目のオカレンスはループの下部に隠されています。

最善のアプローチは、イテレータを使用して、forステートメントを使用してオブジェクトをループできるようにすることです。 たとえば、現在のバージョンのPythonファイルオブジェクトはイテレータプロトコルをサポートしているため、次のように簡単に記述できます。

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

Pythonが一部の機能にメソッドを使用するのはなぜですか(例: list.index())が、他の関数(eg len(list))?

Guidoが言ったように:

(a)一部の演算では、接頭辞表記は後置よりも読みやすくなります。接頭辞(および中置!)演算は、数学者が問題について考えるのにビジュアルが役立つ表記法が好きな数学の長い伝統があります。 x *(a + b)のような式をx * a + x * bに書き直す簡単な方法と、生のOO表記を使用して同じことを行う不器用さを比較してください。

(b)len(x)というコードを読んだとき、何かの長さを要求していることを知っています。 これは私に2つのことを教えてくれます:結果は整数であり、引数はある種のコンテナです。 それどころか、x.len()を読んだとき、xがインターフェイスを実装するか、標準のlen()を持つクラスから継承するある種のコンテナであることをすでに知っている必要があります。 マッピングを実装していないクラスにget()またはkeys()メソッドがある場合、またはファイルではないものにwrite()メソッドがある場合に、ときどき混乱が生じるのを目撃してください。

https://mail.python.org/pipermail/python-3000/2006-November/004643.html


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

文字列は、文字列モジュールの関数を使用して常に利用可能であったのと同じ機能を提供するメソッドが追加されたときに、Python1.6以降の他の標準タイプのようになりました。 これらの新しい方法のほとんどは広く受け入れられていますが、一部のプログラマーに不快感を与えると思われる方法は次のとおりです。

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

結果が得られます:

"1, 2, 4, 8, 16"

この使用法に反対する2つの一般的な議論があります。

最初の行は、「文字列リテラル(文字列定数)のメソッドを使用すると本当に醜いように見えます」という行に沿って実行されます。答えはそうかもしれませんが、文字列リテラルは単なる固定値です。 文字列にバインドされた名前でメソッドを許可する場合、リテラルでメソッドを使用できないようにする論理的な理由はありません。

2番目の異議は通常、「文字列定数を使用してそのメンバーを結合するようにシーケンスに実際に指示しています」としてキャストされます。 悲しいことに、あなたはそうではありません。 何らかの理由で、 split()を文字列メソッドとして使用することの難しさははるかに少ないようです。その場合、それを簡単に確認できるからです。

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

指定された区切り文字(または、デフォルトでは、空白の任意の実行)で区切られたサブ文字列を返す文字列リテラルへの命令です。 この場合、Unicode文字列はUnicode文字列のリストを返し、ASCII文字列はASCII文字列のリストを返し、誰もが満足しています。

join()は文字列メソッドです。これを使用する場合、区切り文字列に一連の文字列を反復処理し、隣接する要素の間に挿入するように指示しているためです。 このメソッドは、自分で定義する可能性のある新しいクラスを含め、シーケンスオブジェクトのルールに従う任意の引数で使用できます。

これは文字列メソッドであるため、Unicode文字列だけでなくプレーンASCII文字列でも機能します。 join()がシーケンスタイプのメソッドである場合、シーケンスタイプは、セパレータのタイプに応じて、返す文字列のタイプを決定する必要があります。

これらの引数のいずれもあなたを説得しない場合は、今のところ、文字列モジュールからjoin()関数を使用し続けることができます。これにより、次のように記述できます。

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

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

例外が発生しない場合、try / exceptブロックは非常に効率的です。 実際に例外をキャッチするのは費用がかかります。 2.0より前のバージョンのPythonでは、次のイディオムを使用するのが一般的でした。

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

これは、dictがほぼ常にキーを持っていると予想した場合にのみ意味がありました。 そうでない場合は、次のようにコーディングしました。

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スタックフレームごとに少なくとも1つのCスタックフレームをプッシュします。 また、拡張機能はほぼランダムな瞬間にPythonにコールバックできます。 したがって、完全なスレッドの実装には、Cのスレッドサポートが必要です。

回答2:幸い、 Stackless Python があります。これは、Cスタックを回避する完全に再設計されたインタープリターループを備えています。


ラムダ式にステートメントを含めることができないのはなぜですか?

Pythonの構文フレームワークは式内にネストされたステートメントを処理できないため、Pythonラムダ式にステートメントを含めることはできません。 ただし、Pythonでは、これは深刻な問題ではありません。 機能を追加する他の言語のラムダフォームとは異なり、Pythonラムダは、関数を定義するのが面倒な場合の省略表記にすぎません。

関数はすでにPythonのファーストクラスのオブジェクトであり、ローカルスコープで宣言できます。 したがって、ローカルで定義された関数の代わりにラムダを使用する唯一の利点は、関数の名前を発明する必要がないことです。ただし、これは、関数オブジェクト(まったく同じタイプのオブジェクト)のローカル変数にすぎません。ラムダ式が生成する)が割り当てられます!


Pythonをマシンコード、C、またはその他の言語にコンパイルできますか?

Cython は、オプションのアノテーションを付けたPythonの修正バージョンをC拡張機能にコンパイルします。 Nuitka は、完全なPython言語をサポートすることを目的とした、PythonをC ++コードに変換する新進気鋭のコンパイラです。 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の標準機能ではないため、移植性がありません。 (はい、BoehmGCライブラリについて知っています。 ほとんどの一般的なプラットフォーム用のアセンブラコードが少し含まれていますが、すべてではありません。ほとんどが透過的ですが、完全に透過的ではありません。 Pythonを動作させるには、パッチが必要です。)

Pythonが他のアプリケーションに組み込まれている場合も、従来のGCが問題になります。 スタンドアロンのPythonでは、標準のmalloc()とfree()をGCライブラリによって提供されるバージョンに置き換えることは問題ありませんが、Pythonを埋め込んだアプリケーションでは、malloc()とfree()の代わりに独自のを使用することができます。 )、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つの数値のタプルとして適切に表されます。

一方、リストは他の言語の配列に似ています。 それらは、すべて同じタイプで、1つずつ操作されるさまざまな数のオブジェクトを保持する傾向があります。 たとえば、os.listdir('.')は、現在のディレクトリ内のファイルを表す文字列のリストを返します。 この出力を操作する関数は、ディレクトリに別のファイルを1つか2つ追加しても、通常は機能しません。

タプルは不変です。つまり、タプルが作成されると、その要素を新しい値に置き換えることはできません。 リストは変更可能です。つまり、リストの要素はいつでも変更できます。 ディクショナリキーとして使用できるのは不変要素のみであるため、キーとして使用できるのはタプルのみであり、リストは使用できません。


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

CPythonのリストは実際には可変長配列であり、Lispスタイルのリンクリストではありません。 実装では、他のオブジェクトへの参照の連続した配列を使用し、この配列へのポインターと配列の長さをリストヘッド構造に保持します。

これにより、リストのインデックス作成a[i]は、コストがリストのサイズやインデックスの値に依存しない操作になります。

アイテムが追加または挿入されると、参照の配列のサイズが変更されます。 アイテムを繰り返し追加するパフォーマンスを向上させるために、ある程度の巧妙さが適用されます。 アレイを拡張する必要がある場合は、追加のスペースが割り当てられるため、次の数回は実際のサイズ変更は必要ありません。


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

CPythonの辞書は、サイズ変更可能なハッシュテーブルとして実装されています。 これにより、Bツリーと比較して、ほとんどの状況でルックアップ(これまでで最も一般的な操作)のパフォーマンスが向上し、実装が簡単になります。

ディクショナリは、 hash()組み込み関数を使用して、ディクショナリに格納されている各キーのハッシュコードを計算することによって機能します。 ハッシュコードはキーによって大きく異なります。 たとえば、「Python」は-539294296にハッシュされ、「python」は1ビット異なる文字列で1142331976にハッシュされます。 次に、ハッシュコードを使用して、値が格納される内部配列内の場所を計算します。 すべてが異なるハッシュ値を持つキーを格納していると仮定すると、これは、辞書がキーを取得するのに一定の時間(コンピューターサイエンス表記ではO(1))を要することを意味します。 これは、キーの並べ替えられた順序が維持されないことも意味し、.keys()および.items()のように配列をトラバースすると、辞書の内容が任意の乱雑な順序で出力されます。


辞書キーが不変でなければならないのはなぜですか?

辞書のハッシュテーブル実装は、キー値から計算されたハッシュ値を使用してキーを検索します。 キーが可変オブジェクトの場合、その値が変更される可能性があるため、ハッシュも変更される可能性があります。 ただし、キーオブジェクトを変更した人は、それがディクショナリキーとして使用されていたことを認識できないため、ディクショナリ内のエントリを移動することはできません。 次に、辞書で同じオブジェクトを検索しようとすると、ハッシュ値が異なるため、そのオブジェクトは見つかりません。 古い値を検索しようとしても、そのハッシュビンで見つかったオブジェクトの値が異なるため、見つかりませんでした。

リストでインデックス付けされた辞書が必要な場合は、最初にリストをタプルに変換するだけです。 関数tuple(L)は、リストLと同じエントリを持つタプルを作成します。 タプルは不変であるため、辞書キーとして使用できます。

提案されているいくつかの容認できない解決策:

  • アドレス(オブジェクトID)ごとのハッシュリスト。 同じ値で新しいリストを作成すると見つからないため、これは機能しません。 例えば:

    mydict = {[1, 2]: '12'}
    print mydict[[../1, 2]]

    2行目で使用されている[1, 2]のIDが最初の行のIDと異なるため、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)の場合、オブジェクトが辞書かどうか。 これらの制限を満たさない場合、辞書やその他のハッシュベースの構造は誤動作します。

ListWrapperの場合、ラッパーオブジェクトがディクショナリにあるときはいつでも、異常を回避するためにラップされたリストを変更してはなりません。 要件とそれらを正しく満たさなかった場合の結果について真剣に考える準備ができていない限り、これを行わないでください。 警告されていると考えてください。


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

パフォーマンスが重要な状況では、リストを並べ替えるためだけにリストのコピーを作成するのは無駄です。 したがって、list.sort()はリストを所定の位置にソートします。 その事実を思い出させるために、ソートされたリストは返されません。 このようにして、ソートされたコピーが必要なときに誤ってリストを上書きすることに騙されることはありませんが、ソートされていないバージョンを保持する必要もあります。

Python 2.4では、新しい組み込み関数 sorted()が追加されました。 この関数は、提供されたiterableから新しいリストを作成し、それをソートして返します。 たとえば、辞書のキーを並べ替えられた順序で繰り返す方法は次のとおりです。

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

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

C ++やJavaなどの言語で提供されるモジュールのインターフェイス仕様では、モジュールのメソッドと関数のプロトタイプが説明されています。 多くの人は、インターフェース仕様のコンパイル時の実施が大規模なプログラムの構築に役立つと感じています。

Python 2.6には、抽象基本クラス(ABC)を定義できる abc モジュールが追加されています。 次に、 isinstance()および issubclass()を使用して、インスタンスまたはクラスが特定のABCを実装しているかどうかを確認できます。 collections モジュールは、 IterableContainerMutableMapping などの便利なABCのセットを定義します。

Pythonの場合、インターフェース仕様の利点の多くは、コンポーネントの適切なテスト分野によって得られます。 サブクラス化による問題を見つけるために使用できるツールPyCheckerもあります。

モジュールの優れたテストスイートは、回帰テストを提供し、モジュールインターフェイスの仕様および一連の例として機能します。 多くのPythonモジュールをスクリプトとして実行して、簡単な「セルフテスト」を提供できます。 複雑な外部インターフェイスを使用するモジュールでさえ、外部インターフェイスの簡単な「スタブ」エミュレーションを使用して、単独でテストできることがよくあります。 doctest および unittest モジュールまたはサードパーティのテストフレームワークを使用して、モジュール内のコードのすべての行を実行する徹底的なテストスイートを構築できます。

適切なテスト規律は、Pythonで大規模で複雑なアプリケーションを構築するのに役立つだけでなく、インターフェイスの仕様を設定するのにも役立ちます。 実際、インターフェイスの仕様ではプログラムの特定のプロパティをテストできないため、より良い場合があります。 たとえば、append()メソッドは、いくつかの内部リストの最後に新しい要素を追加することが期待されています。 インターフェイス仕様では、append()実装が実際にこれを正しく実行するかどうかをテストできませんが、テストスイートでこのプロパティを確認するのは簡単です。

テストスイートを作成することは非常に役立ちます。コードを簡単にテストできるように設計することをお勧めします。 ますます人気が高まっている手法の1つであるテスト指向開発では、実際のコードを作成する前に、まずテストスイートの一部を作成する必要があります。 もちろん、Pythonを使用すると、テストケースをまったく作成せずに、だらしなくすることができます。


なぜ後藤がないのですか?

例外を使用して、関数呼び出し間でも機能する「構造化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の乱用と見なされます。 慎重に使用してください。


生の文字列(r文字列)を円記号で終わらせることができないのはなぜですか?

より正確には、奇数の円記号で終了することはできません。最後の対になっていない円記号は、閉じ引用符をエスケープして、終了していない文字列を残します。

生の文字列は、独自のバックスラッシュエスケープ処理を実行するプロセッサ(主に正規表現エンジン)の入力を簡単に作成できるように設計されています。 このようなプロセッサは、一致しない末尾の円記号をとにかくエラーと見なすため、生の文字列ではそれを許可しません。 その見返りとして、バックスラッシュでエスケープすることにより、文字列の引用符を渡すことができます。 これらのルールは、r文字列が意図された目的で使用される場合にうまく機能します。

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\\"

Pythonに属性割り当て用の「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で実行時に名前バインディングが解決され、2番目のバージョンでは解決を1回だけ実行する必要があるため、実行速度が向上するという副作用もあります。


if / while / def / classステートメントにコロンが必要なのはなぜですか?

コロンは、主に読みやすさを向上させるために必要です(実験的なABC言語の結果の1つ)。 このことを考慮:

if a == b
    print a

if a == b:
    print a

2番目のものが少し読みやすいことに注意してください。 このFAQの回答では、コロンが例をどのように引き立たせているかにさらに注目してください。 これは英語の標準的な使用法です。

もう1つの小さな理由は、コロンを使用すると、構文が強調表示されている編集者にとって簡単になることです。 プログラムテキストのより複雑な解析を行う代わりに、コロンを探してインデントを増やす必要がある時期を決定できます。


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つの要素があるように見えますが、実際には「fee」、「fiefoo」、「fum」の3つが含まれています。 常にコンマを追加すると、このエラーの原因を回避できます。

末尾のコンマを許可すると、プログラムによるコード生成も簡単になる場合があります。