9.9。 クラス
他のプログラミング言語と比較して、Pythonのクラスメカニズムは、最小限の新しい構文とセマンティクスでクラスを追加します。 これは、C ++とModula-3に見られるクラスメカニズムの混合物です。 Pythonクラスは、オブジェクト指向プログラミングのすべての標準機能を提供します。クラス継承メカニズムにより、複数の基本クラスが可能になり、派生クラスはその基本クラスの任意のメソッドをオーバーライドでき、メソッドは同じ名前の基本クラスのメソッドを呼び出すことができます。 。 オブジェクトには、任意の量と種類のデータを含めることができます。 モジュールの場合と同様に、クラスはPythonの動的な性質に関与します。クラスは実行時に作成され、作成後にさらに変更できます。
C ++の用語では、通常、クラスメンバー(データメンバーを含む)は public であり(以下のプライベート変数とクラスローカル参照を参照)、すべてのメンバー関数は virtual [です。 X206X]。 Modula-3と同様に、メソッドからオブジェクトのメンバーを参照するための省略形はありません。メソッド関数は、オブジェクトを表す明示的な最初の引数で宣言されます。これは、呼び出しによって暗黙的に提供されます。 Smalltalkと同様に、クラス自体はオブジェクトです。 これにより、インポートと名前変更のセマンティクスが提供されます。 C ++やModula-3とは異なり、組み込み型はユーザーによる拡張の基本クラスとして使用できます。 また、C ++と同様に、特別な構文(算術演算子、添え字など)を持つほとんどの組み込み演算子は、クラスインスタンスに対して再定義できます。
(クラスについて話すための一般的に受け入れられている用語がないため、SmalltalkおよびC ++の用語をときどき使用します。 オブジェクト指向のセマンティクスはC ++よりもPythonのセマンティクスに近いため、Modula-3の用語を使用しますが、聞いたことのある読者はほとんどいないと思います。)
9.1。 名前とオブジェクトについての一言
オブジェクトには個性があり、(複数のスコープ内の)複数の名前を同じオブジェクトにバインドできます。 これは、他の言語ではエイリアシングとして知られています。 これは通常、Pythonを一目見ただけでは理解できず、不変の基本型(数値、文字列、タプル)を扱う場合は無視しても問題ありません。 ただし、エイリアシングは、リスト、辞書、その他のほとんどのタイプなどの可変オブジェクトを含むPythonコードのセマンティクスに驚くべき影響を与える可能性があります。 エイリアスはいくつかの点でポインタのように動作するため、これは通常、プログラムの利益のために使用されます。 たとえば、オブジェクトの受け渡しは、ポインタのみが実装によって渡されるため、安価です。 また、関数が引数として渡されたオブジェクトを変更すると、呼び出し元に変更が表示されます。これにより、Pascalのように2つの異なる引数を渡すメカニズムが不要になります。
9.2。 Pythonのスコープと名前空間
クラスを紹介する前に、まずPythonのスコープルールについて説明する必要があります。 クラス定義は名前空間でいくつかの巧妙なトリックを果たします。何が起こっているのかを完全に理解するには、スコープと名前空間がどのように機能するかを知る必要があります。 ちなみに、この主題に関する知識は、高度なPythonプログラマーにとって役立ちます。
いくつかの定義から始めましょう。
名前空間は、名前からオブジェクトへのマッピングです。 現在、ほとんどの名前空間はPython辞書として実装されていますが、通常は(パフォーマンスを除いて)目立たないため、将来変更される可能性があります。 名前空間の例は次のとおりです。組み込み名のセット( abs()などの関数と組み込みの例外名を含む)。 モジュール内のグローバル名。 関数呼び出しのローカル名。 ある意味で、オブジェクトの属性のセットも名前空間を形成します。 名前空間について知っておくべき重要なことは、異なる名前空間の名前の間にはまったく関係がないということです。 たとえば、2つの異なるモジュールの両方で、混乱することなく関数maximize
を定義できます。モジュールのユーザーは、その前にモジュール名を付ける必要があります。
ちなみに、ドットに続く名前には attribute という単語を使用します。たとえば、式z.real
では、real
はオブジェクトz
。 厳密に言えば、モジュール内の名前への参照は属性参照です。式modname.funcname
では、modname
はモジュールオブジェクトであり、funcname
はその属性です。 この場合、モジュールの属性とモジュールで定義されたグローバル名の間に簡単なマッピングがあります。それらは同じ名前空間を共有します。 1
属性は読み取り専用または書き込み可能です。 後者の場合、属性への割り当てが可能です。 モジュール属性は書き込み可能です。modname.the_answer = 42
と書くことができます。 書き込み可能な属性は、 del ステートメントを使用して削除することもできます。 たとえば、del modname.the_answer
は、modname
という名前のオブジェクトから属性the_answer
を削除します。
名前空間はさまざまな時点で作成され、さまざまな存続期間があります。 組み込み名を含む名前空間は、Pythonインタープリターの起動時に作成され、削除されることはありません。 モジュールのグローバル名前空間は、モジュール定義が読み込まれるときに作成されます。 通常、モジュールの名前空間もインタプリタが終了するまで続きます。 スクリプトファイルから読み取られるか、インタラクティブにインタプリタのトップレベルの呼び出しによって実行されるステートメントは、 __ main __ と呼ばれるモジュールの一部と見なされるため、独自のグローバル名前空間があります。 (組み込み名は実際にはモジュール内にも存在します。これは __ builtin __ と呼ばれます。)
関数のローカル名前空間は、関数が呼び出されたときに作成され、関数が関数内で処理されない例外を返すか発生したときに削除されます。 (実際には、忘却は実際に何が起こるかを説明するためのより良い方法です。)もちろん、再帰呼び出しにはそれぞれ独自のローカル名前空間があります。
scope は、名前空間に直接アクセスできるPythonプログラムのテキスト領域です。 ここでの「直接アクセス可能」とは、名前への修飾されていない参照が名前空間で名前を見つけようとすることを意味します。
スコープは静的に決定されますが、動的に使用されます。 実行中はいつでも、名前空間に直接アクセスできるネストされたスコープが少なくとも3つあります。
- 最初に検索される最も内側のスコープには、ローカル名が含まれます
- 最も近い囲みスコープから検索される囲み関数のスコープには、非ローカル名だけでなく非グローバル名も含まれます
- 最後から2番目のスコープには、現在のモジュールのグローバル名が含まれます
- 最も外側のスコープ(最後に検索)は、組み込み名を含む名前空間です
名前がグローバルとして宣言されている場合、すべての参照と割り当ては、モジュールのグローバル名を含む中央のスコープに直接移動します。 それ以外の場合、最も内側のスコープの外側にあるすべての変数は読み取り専用です(このような変数に書き込もうとすると、最も内側のスコープに new ローカル変数が作成され、同じ名前の外側の変数は変更されません)。
通常、ローカルスコープは(テキストで)現在の関数のローカル名を参照します。 関数の外部では、ローカルスコープはグローバルスコープと同じ名前空間、つまりモジュールの名前空間を参照します。 クラス定義は、ローカルスコープにさらに別の名前空間を配置します。
スコープはテキストで決定されることを理解することが重要です。モジュールで定義された関数のグローバルスコープは、関数がどこから、またはどのエイリアスによって呼び出されたかに関係なく、そのモジュールの名前空間です。 一方、名前の実際の検索は実行時に動的に実行されますが、言語定義は「コンパイル」時に静的な名前解決に向かって進化しているため、動的な名前解決に依存しないでください。 (実際、ローカル変数はすでに静的に決定されています。)
Pythonの特別な癖は、 global ステートメントが有効になっていない場合、名前への割り当てが常に最も内側のスコープに入ることです。 割り当てはデータをコピーしません—名前をオブジェクトにバインドするだけです。 削除についても同じことが言えます。ステートメントdel x
は、ローカルスコープによって参照される名前空間からx
のバインディングを削除します。 実際、新しい名前を導入するすべての操作はローカルスコープを使用します。特に、 import ステートメントと関数定義は、ローカルスコープ内のモジュールまたは関数名をバインドします。 ( global ステートメントを使用して、特定の変数がグローバルスコープに存在することを示すことができます。)
9.3。 クラスの初見
クラスは、少しの新しい構文、3つの新しいオブジェクト型、およびいくつかの新しいセマンティクスを導入します。
9.3.1。 クラス定義構文
クラス定義の最も単純な形式は次のようになります。
関数定義( def ステートメント)のようなクラス定義は、効果を発揮する前に実行する必要があります。 (おそらく、クラス定義を if ステートメントのブランチまたは関数内に配置できます。)
実際には、クラス定義内のステートメントは通常関数定義ですが、他のステートメントも許可されており、場合によっては便利です。後でこれに戻ります。 クラス内の関数定義には通常、メソッドの呼び出し規約によって決定される、独特の形式の引数リストがあります。これについても、後で説明します。
クラス定義が入力されると、新しい名前空間が作成され、ローカルスコープとして使用されます。したがって、ローカル変数へのすべての割り当ては、この新しい名前空間に入ります。 特に、関数定義はここで新しい関数の名前をバインドします。
クラス定義が(最後から)正常に残されると、クラスオブジェクトが作成されます。 これは基本的に、クラス定義によって作成された名前空間のコンテンツのラッパーです。 次のセクションでは、クラスオブジェクトについて詳しく学習します。 元のローカルスコープ(クラス定義が入力される直前に有効だったスコープ)が復元され、クラスオブジェクトはここでクラス定義ヘッダー(例ではClassName
)で指定されたクラス名にバインドされます。
9.3.2。 クラスオブジェクト
クラスオブジェクトは、属性参照とインスタンス化の2種類の操作をサポートします。
属性参照は、Pythonのすべての属性参照に使用される標準構文obj.name
を使用します。 有効な属性名は、クラスオブジェクトが作成されたときにクラスの名前空間にあったすべての名前です。 したがって、クラス定義が次のようになっている場合:
その場合、MyClass.i
とMyClass.f
は有効な属性参照であり、それぞれ整数と関数オブジェクトを返します。 クラス属性も割り当てることができるので、割り当てによってMyClass.i
の値を変更できます。 __doc__
も有効な属性であり、クラス"A simple example class"
に属するdocstringを返します。
クラスインスタンス化は関数表記を使用します。 クラスオブジェクトが、クラスの新しいインスタンスを返すパラメータのない関数であると偽ってください。 例(上記のクラスを想定):
クラスの新しいインスタンスを作成し、このオブジェクトをローカル変数x
に割り当てます。
インスタンス化操作(クラスオブジェクトの「呼び出し」)により、空のオブジェクトが作成されます。 多くのクラスは、特定の初期状態にカスタマイズされたインスタンスを使用してオブジェクトを作成することを好みます。 したがって、クラスは次のように__init__()
という名前の特別なメソッドを定義できます。
クラスが__init__()
メソッドを定義すると、クラスのインスタンス化により、新しく作成されたクラスインスタンスに対して__init__()
が自動的に呼び出されます。 したがって、この例では、新しい初期化されたインスタンスを次の方法で取得できます。
もちろん、__init__()
メソッドには、柔軟性を高めるための引数が含まれている場合があります。 その場合、クラスインスタンス化演算子に与えられた引数は__init__()
に渡されます。 例えば、
9.3.3。 インスタンスオブジェクト
では、インスタンスオブジェクトで何ができるでしょうか。 インスタンスオブジェクトによって理解される唯一の操作は、属性参照です。 有効な属性名には、データ属性とメソッドの2種類があります。
データ属性は、Smalltalkの「インスタンス変数」、およびC ++の「データメンバー」に対応します。 データ属性を宣言する必要はありません。 ローカル変数のように、それらは最初に割り当てられたときに存在します。 たとえば、x
が上記で作成したMyClass
のインスタンスである場合、次のコードはトレースを残さずに値16
を出力します。
他の種類のインスタンス属性参照は、メソッドです。 メソッドは、オブジェクトに「属する」関数です。 (Pythonでは、メソッドという用語はクラスインスタンスに固有ではありません。他のオブジェクトタイプにもメソッドを含めることができます。 たとえば、リストオブジェクトには、append、insert、remove、sortなどのメソッドがあります。 ただし、以下の説明では、特に明記されていない限り、メソッドという用語は、クラスインスタンスオブジェクトのメソッドを意味するためにのみ使用します。)
インスタンスオブジェクトの有効なメソッド名は、そのクラスによって異なります。 定義上、関数オブジェクトであるクラスのすべての属性は、そのインスタンスの対応するメソッドを定義します。 したがって、この例では、MyClass.f
は関数であるため、x.f
は有効なメソッド参照ですが、MyClass.i
は関数ではないため、x.i
はそうではありません。 ただし、x.f
はMyClass.f
と同じものではなく、メソッドオブジェクトであり、関数オブジェクトではありません。
9.3.4。 メソッドオブジェクト
通常、メソッドはバインドされた直後に呼び出されます。
MyClass
の例では、これは文字列'hello world'
を返します。 ただし、メソッドをすぐに呼び出す必要はありません。x.f
はメソッドオブジェクトであり、保存して後で呼び出すことができます。 例えば:
時間の終わりまでhello world
を印刷し続けます。
メソッドが呼び出されると、正確にはどうなりますか? f()
の関数定義で引数が指定されていても、x.f()
が上記の引数なしで呼び出されたことにお気づきかもしれません。 議論はどうなりましたか? 確かに、引数を必要とする関数が何もなしで呼び出されると、Pythonは例外を発生させます—引数が実際に使用されていなくても…
実際、あなたは答えを推測したかもしれません:メソッドの特別なことは、オブジェクトが関数の最初の引数として渡されることです。 この例では、呼び出しx.f()
はMyClass.f(x)
とまったく同じです。 一般に、 n 引数のリストを使用してメソッドを呼び出すことは、最初の引数の前にメソッドのオブジェクトを挿入することによって作成された引数リストを使用して対応する関数を呼び出すことと同じです。
それでもメソッドがどのように機能するかを理解していない場合は、実装を見ると問題が明らかになる可能性があります。 インスタンスの非データ属性が参照されると、インスタンスのクラスが検索されます。 名前が関数オブジェクトである有効なクラス属性を示している場合、メソッドオブジェクトは、インスタンスオブジェクトと抽象オブジェクトで一緒に見つかった関数オブジェクトをパック(ポインタ)することによって作成されます。これがメソッドオブジェクトです。 メソッドオブジェクトが引数リストで呼び出されると、インスタンスオブジェクトと引数リストから新しい引数リストが作成され、この新しい引数リストで関数オブジェクトが呼び出されます。
9.3.5。 クラス変数とインスタンス変数
一般的に、インスタンス変数は各インスタンスに固有のデータ用であり、クラス変数はクラスのすべてのインスタンスによって共有される属性とメソッド用です。
名前とオブジェクトについての単語で説明されているように、共有データは、リストや辞書などの可変オブジェクトを含むことで驚くべき効果をもたらす可能性があります。 たとえば、次のコードの tricks リストは、すべての Dog インスタンスで共有されるリストが1つだけであるため、クラス変数として使用しないでください。
クラスの正しい設計では、代わりにインスタンス変数を使用する必要があります。
9.4。 ランダムな発言
データ属性は、同じ名前のメソッド属性をオーバーライドします。 大規模なプログラムで見つけにくいバグを引き起こす可能性のある偶発的な名前の競合を回避するために、競合の可能性を最小限に抑える何らかの規則を使用することをお勧めします。 考えられる規則には、メソッド名の大文字化、データ属性名の前に小さな一意の文字列(おそらくアンダースコア)を付ける、メソッドに動詞を使用し、データ属性に名詞を使用するなどがあります。
データ属性は、オブジェクトの通常のユーザー(「クライアント」)だけでなく、メソッドによっても参照できます。 言い換えると、クラスは純粋な抽象データ型を実装するために使用できません。 実際、Pythonにはデータの非表示を強制することを可能にするものはなく、すべて慣例に基づいています。 (一方、Cで記述されたPython実装は、実装の詳細を完全に非表示にし、必要に応じてオブジェクトへのアクセスを制御できます。これは、Cで記述されたPythonの拡張機能で使用できます。)
クライアントは注意してデータ属性を使用する必要があります—クライアントは、データ属性にスタンプを付けることによって、メソッドによって維持される不変条件を台無しにする可能性があります。 名前の競合が回避される限り、クライアントはメソッドの有効性に影響を与えることなく、独自のデータ属性をインスタンスオブジェクトに追加できることに注意してください。ここでも、命名規則によって多くの問題を回避できます。
メソッド内からデータ属性(または他のメソッド!)を参照するための省略形はありません。 これにより、実際にメソッドの可読性が向上することがわかりました。メソッドを一瞥するときに、ローカル変数とインスタンス変数を混同する可能性はありません。
多くの場合、メソッドの最初の引数はself
と呼ばれます。 これは単なる慣習にすぎません。self
という名前はPythonにとって特別な意味はまったくありません。 ただし、規則に従わないと、他のPythonプログラマーがコードを読みにくくなる可能性があり、そのような規則に依存するクラスブラウザープログラムが作成される可能性があることに注意してください。
クラス属性である関数オブジェクトは、そのクラスのインスタンスのメソッドを定義します。 関数定義がテキストでクラス定義に含まれている必要はありません。関数オブジェクトをクラス内のローカル変数に割り当てることもできます。 例えば:
f
、g
、h
はすべて、関数オブジェクトを参照するクラスC
の属性であるため、これらはすべてC
— h
はg
とまったく同じです。 この方法は通常、プログラムの読者を混乱させるだけであることに注意してください。
メソッドは、self
引数のメソッド属性を使用して、他のメソッドを呼び出すことができます。
メソッドは、通常の関数と同じ方法でグローバル名を参照できます。 メソッドに関連付けられたグローバルスコープは、その定義を含むモジュールです。 (クラスがグローバルスコープとして使用されることはありません。)メソッドでグローバルデータを使用する正当な理由に遭遇することはめったにありませんが、グローバルスコープの正当な使用法はたくさんあります。たとえば、グローバルスコープにインポートされた関数とモジュールは次のことができます。メソッド、およびメソッドで定義されている関数とクラスによって使用されます。 通常、メソッドを含むクラス自体はこのグローバルスコープで定義されます。次のセクションでは、メソッドが独自のクラスを参照する理由をいくつか説明します。
各値はオブジェクトであるため、クラス(タイプとも呼ばれます)があります。 object.__class__
として保存されます。
9.5。 継承
もちろん、言語機能は、継承をサポートしない限り、「クラス」という名前に値するものではありません。 派生クラス定義の構文は次のようになります。
名前BaseClassName
は、派生クラス定義を含むスコープで定義する必要があります。 基本クラス名の代わりに、他の任意の式も使用できます。 これは、たとえば、基本クラスが別のモジュールで定義されている場合に役立ちます。
派生クラス定義の実行は、基本クラスの場合と同じように進行します。 クラスオブジェクトが作成されると、基本クラスが記憶されます。 これは、属性参照を解決するために使用されます。要求された属性がクラスで見つからない場合、検索は基本クラスの検索に進みます。 このルールは、基本クラス自体が他のクラスから派生している場合に再帰的に適用されます。
派生クラスのインスタンス化について特別なことは何もありません。DerivedClassName()
はクラスの新しいインスタンスを作成します。 メソッド参照は次のように解決されます。対応するクラス属性が検索され、必要に応じて基本クラスのチェーンを下っていきます。メソッド参照は、関数オブジェクトが生成される場合に有効です。
派生クラスは、基本クラスのメソッドをオーバーライドする場合があります。 同じオブジェクトの他のメソッドを呼び出す場合、メソッドには特別な特権がないため、同じ基本クラスで定義された別のメソッドを呼び出す基本クラスのメソッドは、それをオーバーライドする派生クラスのメソッドを呼び出すことになります。 (C ++プログラマーの場合:Pythonのすべてのメソッドは事実上virtual
です。)
派生クラスのオーバーライドメソッドは、実際には、同じ名前の基本クラスメソッドを単に置き換えるのではなく、拡張したい場合があります。 基本クラスのメソッドを直接呼び出す簡単な方法があります。BaseClassName.methodname(self, arguments)
を呼び出すだけです。 これは、クライアントにも役立つ場合があります。 (これは、基本クラスがグローバルスコープでBaseClassName
としてアクセスできる場合にのみ機能することに注意してください。)
Pythonには、継承で機能する2つの組み込み関数があります。
- isinstance()を使用して、インスタンスのタイプを確認します。
obj.__class__
が int または派生したクラスの場合にのみ、isinstance(obj, int)
はTrue
になります。 int から。 - issubclass()を使用して、クラスの継承を確認します。 bool は int のサブクラスであるため、
issubclass(bool, int)
はTrue
です。 ただし、 unicode は str のサブクラスではないため、issubclass(unicode, str)
はFalse
です(共通の祖先である basestring のみを共有します)。 )。
9.5.1。 多重継承
Pythonは、限定された形式の多重継承もサポートしています。 複数の基本クラスを持つクラス定義は次のようになります。
古いスタイルのクラスの場合、唯一のルールは深さ優先、左から右です。 したがって、属性がDerivedClassName
で見つからない場合は、Base1
で検索され、次にBase1
の基本クラスで(再帰的に)検索されます。そこでは、Base2
などで検索されます。
(幅優先探索(Base1
の基本クラスの前にBase2
およびBase3
を検索する)は、より自然に見える人もいます。 ただし、これには、名前と属性の競合の結果を理解する前に、Base1
の特定の属性が実際にBase1
またはその基本クラスの1つで定義されているかどうかを知る必要があります。 Base2
の。 深さ優先ルールでは、Base1
の直接属性と継承属性に違いはありません。)
新しいスタイルのクラスの場合、メソッドの解決順序は動的に変更され、 super()への協調呼び出しをサポートします。 このアプローチは、他のいくつかの多重継承言語ではcall-next-methodとして知られており、単一継承言語で見られるスーパーコールよりも強力です。
新しいスタイルのクラスでは、多重継承のすべてのケースで1つ以上のひし形の関係が示されるため、動的な順序付けが必要です(親クラスの少なくとも1つは、最下位のクラスから複数のパスを介してアクセスできます)。 たとえば、すべての新しいスタイルのクラスはオブジェクトから継承するため、多重継承の場合は、オブジェクトに到達するための複数のパスが提供されます。 基本クラスが複数回アクセスされないようにするために、動的アルゴリズムは、各クラスで指定された左から右への順序を保持する方法で検索順序を線形化し、各親を1回だけ呼び出し、単調です(つまり、クラスは、その親の優先順位に影響を与えることなくサブクラス化できます)。 これらのプロパティを総合すると、多重継承を備えた信頼性が高く拡張可能なクラスを設計できます。 詳細については、 https://www.python.org/download/releases/2.3/mro/を参照してください。
9.6。 プライベート変数とクラスローカル参照
オブジェクトの内部以外からアクセスできない「プライベート」インスタンス変数は、Pythonには存在しません。 ただし、ほとんどのPythonコードが従う規則があります。名前の前にアンダースコアが付いています(例: _spam
)は、APIの非公開部分として扱う必要があります(関数、メソッド、またはデータメンバーのいずれであっても)。 これは実装の詳細と見なされ、予告なしに変更される場合があります。
クラスプライベートメンバーには有効なユースケースがあるため(つまり、サブクラスによって定義された名前と名前の名前の衝突を回避するため)、 name mangling と呼ばれるそのようなメカニズムのサポートは制限されています。 __spam
の形式の識別子(少なくとも2つの先頭のアンダースコア、最大で1つの末尾のアンダースコア)は、テキストで_classname__spam
に置き換えられます。ここで、classname
は先頭のアンダースコアを持つ現在のクラス名です。 (s)剥ぎ取られた。 このマングリングは、クラスの定義内で発生する限り、識別子の構文上の位置に関係なく実行されます。
名前マングリングは、クラス内メソッド呼び出しを中断せずにサブクラスがメソッドをオーバーライドできるようにするのに役立ちます。 例えば:
上記の例は、MappingSubclass
がMapping
クラスおよび_MappingSubclass__update
で_Mapping__update
に置き換えられているため、__update
識別子を導入した場合でも機能します。 ]それぞれMappingSubclass
クラスにあります。
マングリングルールは主に事故を避けるために設計されていることに注意してください。 プライベートと見なされる変数にアクセスしたり、変更したりすることは引き続き可能です。 これは、デバッガーなどの特別な状況でも役立ちます。
exec
、eval()
、またはexecfile()
に渡されるコードは、呼び出し元のクラスのクラス名を現在のクラスとは見なさないことに注意してください。 これは、global
ステートメントの効果に似ており、その効果は同様に、一緒にバイトコンパイルされるコードに制限されます。 getattr()
、setattr()
、delattr()
、および__dict__
を直接参照する場合にも同じ制限が適用されます。
9.7。 オッズとエンド
Pascalの「record」またはCの「struct」に似たデータ型を使用して、いくつかの名前付きデータ項目をバンドルすると便利な場合があります。 空のクラス定義はうまく機能します:
特定の抽象データ型を期待するPythonコードの一部には、代わりにそのデータ型のメソッドをエミュレートするクラスを渡すことができます。 たとえば、ファイルオブジェクトから一部のデータをフォーマットする関数がある場合、代わりに文字列バッファーからデータを取得して渡すメソッドread()
およびreadline()
を使用してクラスを定義できます。引数として。
インスタンスメソッドオブジェクトにも属性があります。m.im_self
はメソッドm()
のインスタンスオブジェクトであり、m.im_func
はメソッドに対応する関数オブジェクトです。
9.8。 例外はクラスもあります
ユーザー定義の例外は、クラスによっても識別されます。 このメカニズムを使用すると、例外の拡張可能な階層を作成することができます。
raise ステートメントには2つの新しい有効な(セマンティック)フォームがあります。
最初の形式では、instance
はClass
またはそれから派生したクラスのインスタンスである必要があります。 2番目の形式は、次の省略形です。
exception 句のクラスは、同じクラスまたはその基本クラスである場合、例外と互換性があります(ただし、その逆ではありません。派生クラスをリストするexcept句は基本クラスと互換性がありません)。 )。 たとえば、次のコードはB、C、Dをこの順序で出力します。
例外句が逆になっている場合(except B
が最初)、B、B、Bが出力されることに注意してください—最初に一致する例外句がトリガーされます。
未処理の例外に対してエラーメッセージが出力されると、例外のクラス名が出力され、次にコロンとスペースが出力され、最後に組み込み関数 str()を使用してインスタンスが文字列に変換されます。
9.9。 イテレータ
これまでに、ほとんどのコンテナオブジェクトが for ステートメントを使用してループオーバーできることに気付いたと思います。
このスタイルのアクセスは、明確、簡潔、そして便利です。 イテレータの使用はPythonに浸透し、統合されています。 舞台裏では、 for ステートメントがコンテナオブジェクトの iter()を呼び出します。 この関数は、コンテナ内の要素に一度に1つずつアクセスするメソッド next()を定義するイテレータオブジェクトを返します。 要素がなくなると、 next()はStopIteration
例外を発生させ、 for ループを終了するように指示します。 この例は、すべてがどのように機能するかを示しています。
イテレータプロトコルの背後にある仕組みを見てきたので、クラスにイテレータの動作を追加するのは簡単です。 next()メソッドでオブジェクトを返す__iter__()
メソッドを定義します。 クラスが next()を定義している場合、__iter__()
はself
を返すことができます。
9.10。 発電機
Generator は、イテレータを作成するためのシンプルで強力なツールです。 これらは通常の関数のように記述されていますが、データを返したいときはいつでも yield ステートメントを使用します。 next()が呼び出されるたびに、ジェネレーターは中断したところから再開します(すべてのデータ値と最後に実行されたステートメントを記憶します)。 例は、ジェネレーターを簡単に作成できることを示しています。
前のセクションで説明したように、ジェネレーターで実行できることはすべて、クラスベースのイテレーターでも実行できます。 ジェネレーターを非常にコンパクトにしているのは、__iter__()
メソッドと next()メソッドが自動的に作成されることです。
もう1つの重要な機能は、ローカル変数と実行状態が呼び出し間で自動的に保存されることです。 これにより、self.index
やself.data
などのインスタンス変数を使用するアプローチよりも、関数の記述が簡単になり、はるかに明確になりました。
メソッドの自動作成とプログラム状態の保存に加えて、ジェネレーターが終了すると、StopIteration
が自動的に発生します。 これらの機能を組み合わせることで、通常の関数を作成するだけでイテレーターを簡単に作成できます。
9.11。 ジェネレータ式
一部の単純なジェネレーターは、リスト内包表記に似た構文を使用して式として簡潔にコーディングできますが、角括弧の代わりに括弧を使用します。 これらの式は、ジェネレーターが囲み関数によってすぐに使用される状況向けに設計されています。 ジェネレータ式は、完全なジェネレータ定義よりもコンパクトですが、汎用性が低く、同等のリスト内包表記よりもメモリに優しい傾向があります。
例:
脚注
- 1
- 1つを除いて。 モジュールオブジェクトには、 __ dict __ と呼ばれる秘密の読み取り専用属性があり、モジュールの名前空間を実装するために使用されるディクショナリを返します。 名前 __ dict __ は属性ですが、グローバル名ではありません。 明らかに、これを使用すると名前空間の実装の抽象化に違反するため、事後デバッガーなどに制限する必要があります。