9. クラス

Python のクラスの仕組みは、 最小限の新しい構文と意味をもって言語にクラスを追加している。 それは C++ と Modula-3 に見られるクラスの仕組みの混合だ。 モジュールがそうであるように、Python のクラスは定義と利用者のあいだに 絶対的な障壁を置くことはせず、むしろ ``定義内部に乱入する'' ことは しないという利用者の礼儀正しさに頼っている。 それでも、クラスの最も重要な機能は、強力さを損なうことなく保たれている。 すなわち、クラス継承の仕組みは多重の基底クラスを許していて、 派生クラスはその基底クラスのどのメソッドも上書きする (override) ことができ、 メソッド呼出しは基底クラスの同名のメソッドを呼び出すことができる。 オブジェクトは任意の量の私有データを持つことができる。

C++ の用語でいえば、 クラスメンバは (データメンバも含め) すべて公開 (public) で、 メンバ関数はすべて仮想 (virtual) だ。 特別なコンストラクタやデストラクタはない。 Modula-3 と同じく、オブジェクトのメンバをそのメソッドから参照するための 短縮記法はない。すなわち、 メソッド関数はそのオブジェクトをあらわす第1引数を明示的に伴って宣言される。 この第1引数は呼出しによって自動的に供給される。 言葉の広い意味においてではあるが、 Smalltalk と同じく、クラスはそれ自身がオブジェクトだ。 さらにいえば、Python では、すべてのデータ型がオブジェクトだ。 このことが輸入 (importing) と改名 (renaming) の意味論を規定している。 しかし、C++ や Modula-3 と全く同じく、 組込み型を基底クラスとして使って利用者が拡張することはできない。 また、C++ と同じく、Modula-3 とは違って、 (算術演算子や添字付けなど) 特別な構文をもつ組込み演算子の大多数を クラスインスタンスのために再定義することができる。

 
9.1 用語について一言

クラスについて語るための普遍的に受け入れられている用語がないので、 私は Smalltalk と C++ の用語を場合に応じて使っていくことに する (オブジェクト指向の意味論は C++よりも Modula-3 のほうが Python に 近いから、Modula-3 の用語を使いたかったが、 ほとんどの読者はそれを耳にしたことがないだろうと思う)。

私はまた、オブジェクト指向派の読者にとって用語上の落し穴があることを、 君に警告しなくてならない。 すなわち、``オブジェクト'' という言葉は Python では 必ずしもクラスのインスタンスを意味しない。 C++ や Modula-3 と同じく、Smalltalk とは違って、Python では すべての型がクラスだというわけではない。 整数やリストのような基本的な組込み型はクラスではなく、 ファイルのような幾分珍しい型ですらクラスではない。 それでも、Python のすべての型は、オブジェクトという言葉を使って 記述するのが最適な、ちょっとした共通の意味論を共有している。

オブジェクトは個体として存在していて、 (別々のスコープで) 別々の名前が同一のオブジェクトへと束縛され得る。 これは、ほかの言語では別名 (aliasing) として知られている。 そのありがたみは普通は Python をひとめ見ただけでは分からないし、 変化不可能な基本型 (数、文字列、タプル) を扱うときには無視しても差し支えない。 しかし、別名には、変化可能オブジェクト -- リストや、 辞書や、プログラム外部の実体 (ファイルやウィンドウなど) を表現するほとんどの 型 -- に関係する Python コードの意味論に関して、ある (意図的な!) 効果がある。 これは普通はプログラムのためになるように使われる。 なぜなら別名は、ある意味、ポインタのように振舞うからだ。 たとえば、オブジェクトの受渡しは、 実装ではポインタが渡されるだけだから安価だ。 そして、もし関数が引数として渡されたオブジェクトを改変したなら、 呼出した側からその変化が見られることになる -- これは Pascal にあるような 二つの異なった引数渡しの仕組みの必要性をなくしている。

 
9.2 Python のスコープと名前空間

クラスを紹介する前に、 まず Python のスコープ規則についてなにがしか話さなくてはならない。 クラス定義はある巧みなトリックを名前空間に対して演じるから、 何が起こっているのかを完全に理解するには、 スコープと名前空間の働きかたを知る必要がある。 ついでながら、この件についての知識はあらゆる上級 Python プログラマの役に立つ。

まず定義から始めよう。

名前空間 (namespace) とは、 名前からオブジェクトへの写像 (mapping) だ。 ほとんどの名前空間は今のところ Python の辞書として実装されているが、 そのことは通常どのみち (性能を除き) 重要ではないし、 将来は変更されるかもしれない。 名前空間の例としては、 組込み名の集合 (abs() 等の関数や組込み例外名)、 1モジュールにおけるグローバル名の集合、 1関数呼出しにおけるローカル名の集合がある。 ある意味では1オブジェクトの属性 (attribute) の集合も1名前空間を形成している。 名前空間について知っておくべき重要なことは、 別々の名前空間にある名前どうしは絶対的に無関係だ、ということだ。 たとえば、二つの別々のモジュールが、混同することなく、 ともに ``maximize'' という関数を定義してもよい -- モジュールの 利用者はそれにモジュール名を接頭しなくてはならない。

ところで、属性という言葉を、なんであれドットに続く名前に対して 使っている -- たとえば式 z.real で、 real はオブジェクト z の属性だ。 厳密にいえば、モジュールの中にある名前を参照することは、属性参照だ。 つまり、式 modname.funcname で、 modname は1モジュールオブジェクトであり、 funcname はその1属性となる。 この場合はたまたま、モジュールの属性と、 モジュールで定義されているグローバル名のあいだに、素直な写像がある。 すなわち、それらは同じ名前空間を共有している! 9.1

属性は読取り専用かもしれないし書込み可能かもしれない。 後者の場合、属性への代入が可能だ。 モジュール属性は書込み可能だ。 たとえば、 "modname.the_answer = 42" と書くことができる。 書込み可能属性は、del 文で削除することもできる。 たとえば "del modname.the_answer" は、modname によって指定 されたオブジェクトから属性 the_answer を取り除く。

名前空間が造られる時 (moment) と寿命 (lifetime) はさまざまだ。 組込み名を収めている名前空間は、Python インタープリタが起動する時に造られ、 そして決して削除されない。 モジュールのためのグローバル名前空間は、 モジュール定義が読み込まれた時に造られ、 そして通常これもインタープリタが終わるまで存続する。 インタープリタのトップレベル呼出しによって実行される文は、 スクリプトファイルから読まれたにせよ、対話的に読まれたにせよ、 __main__ という1モジュールの一部であると見なされる。 だから、それらにもそれら自身のグローバル名前空間がある。 (組込み名も、実際には __builtin__ という1モジュールの中に住んでいる)

関数のためのローカル名前空間は、関数が呼び出された時に造られ、 そして関数から戻るか、またはその関数内で処理されない例外をひき起した時に 削除される (実際には、忘れられる、と言ったほうが実態に近い)。 もちろん、再帰呼出しのときも、それぞれの呼出しごとにローカル名前空間がある。

スコープ (scope) とは、ある名前空間を直接アクセス可能 な、Python プログラムの字面上の領域 (textual region) の事だ。 ここで、ある名前空間を ``直接アクセス可能'' (directly accessible) とは、 接頭辞なしに名前を参照したとき、その名前空間で名前の検索が試みられることを 意味する。

スコープは静的に決定されるが、動的に使用される。 実行中はいつでも、その名前空間が直接アクセス可能な少なくとも3つの入れ 子のスコープがある。 最初に検索される最内スコープがローカル名を収め、 スコープ内から検索したもっとも近い任意の関数内の名前空間、 次に検索される中間スコープが現在のモジュールのグローバル名を収め、 そして (最後に検索される) 最外スコープが組込み名を収めている名前空間だ。

名前がグローバルに定義されている場合、参照および割り当てはすべて、モジュー ルのグローバル名を収めている中間スコープへ直接アクセスする。 そうでなければ、最内スコープの外部で見つかった変数はすべて読み出し専用 となる。

普通、ローカルスコープは現在の関数--字面的に--のローカル名を参照する。 関数の外側では、ローカルスコープはグローバルスコープと同じ名前空間、 すなわちモジュールの名前空間を参照する。 クラス定義はローカルスコープの中にもう一つの名前空間を設ける。

重要なのは、スコープは字面で決定される、ということをはっきり理解することだ。 たとえば、あるモジュールの中で定義された関数のグローバルスコープは、 たとえその関数がどこから、またはどんな別名によって、呼び出されようとも、 そのモジュールの名前空間だ。 他方、実際の名前検索は、実行時に、動的に行われる -- しかし、 言語の定義は、``コンパイル'' 時の、静的な名前解決へ向けて進化しているから、 動的な名前解決に頼ってはならない! (事実、ローカル変数は既に 静的に決定されている)

Python の特別なクセとして、代入はいつも最内スコープに入る。 代入はデータをコピーしない -- ただ名前をオブジェクトへ束縛する。 同じことが削除にもあてはまる。たとえば、文 "del x" は、x の束縛を、 ローカルスコープが参照する名前空間から削除する。 実際、新しい名前を導入する演算はすべてローカルスコープを用いる。とりわけ、 import 文と関数定義は、モジュールや関数名を、ローカルスコープで束縛する。 (global 文を使えば、 ある変数がグローバルスコープに住んでいることを指示できる)

 
9.3 クラス初見

クラスはちょっとした新しい構文、三つの新しいオブジェクト型、そしていくつかの 新しい意味論を導入している。

 
9.3.1 クラス定義の構文

最も簡単な形式のクラス定義はこんな形をしている。

class ClassName:
    <文-1>
    .
    .
    .
    <文-N>

クラス定義は、関数定義 (def 文) と同じく、 実行されて初めて効果がある。 (if 文の分岐や関数内部にクラス定義を置くことも、 考えられる限りではあり得る)

実際には、クラス定義内部の文は普通は関数定義だろう。 しかし、ほかの文であってもよいし、 それが役に立つこともある -- これについては後述する。 クラス内部の関数定義は通常は、メソッドの呼出し規約に従った、ある特定の形式の 引数並びをもつ -- これもまた後述する。

クラス定義へ進入する時、新しい名前空間が造られ、 ローカルスコープとして使われる -- したがって、 ローカル変数への代入はすべてこの新しい名前空間へ入る。 とりわけ、関数定義は新しい関数の名前をここで束縛する。

クラス定義から (終端を経由して) 正常に退出する時、 クラスオブジェクト (class object) が造られる。 これは基本的には、クラス定義が造った名前空間の内容を くるんだラッパー (wrapper) だ。 クラスオブジェクトについては次の節でさらに学ぶことにしよう。 元々のローカルスコープ (クラス定義へ進入する直前まで有効だったスコープ) が 復帰し、そしてこのスコープにおいて、クラスオブジェクトが、 クラス定義ヘッダで与えられた名前 (上の例でいえば ClassName) へ 束縛される。

 
9.3.2 クラスオブジェクト

クラスオブジェクトは2種類の演算 -- 属性参照と インスタンス生成 -- をサポートしている。

属性参照 (attribute reference) は、Python におけるすべての属性参照で 使われている標準的な構文 obj.name を使う。 妥当な属性名は、クラスオブジェクトが造られた時に クラスの名前空間にあった名前すべてだ。 だから、もしも次のようなクラス定義なら、

class MyClass:
    "A simple example class"
    i = 12345
    def f(self):
        return 'hello world'

MyClass.iMyClass.f は妥当な属性参照で、 それぞれ整数とメソッドオブジェクトを返す。 クラス属性へ代入してもよい。 だから君は MyClass.i の値を代入によって変えられる。 __doc__ も妥当な属性で、そのクラスに属している docstring、 この場合は "A simple example class" を返す。

クラスのインスタンス生成 (instantiation) は関数記法を使う。 クラスオブジェクトのことを、 クラスの新しいインスタンスを返す無引数関数だと思い込めばよい。 たとえば (上記のクラスでいえば)、

x = MyClass()

はクラスの新しいインスタンス (instance) を生成し、 そのオブジェクトをローカル変数 x へ代入する。

インスタンス生成演算 (クラスオブジェクトの ``呼出し'') は、 空のオブジェクト (empty object) を造る。 多くのクラスでは既知の初期状態にオブジェクトを造ることが望ましい。 したがってクラスは __init__() という名前の特別なメソッドを、 このように定義してよい。

    def __init__(self):
        self.data = []

クラスが __init__() メソッドを定義しているとき、 クラスのインスタンス生成は、新しく造られたクラスインスタンスに対して 自動的に __init__() を呼び出す。 だからこの例では、新しい、初期化済みのインスタンスが下記によって得られる。

x = MyClass()

もちろん、より大きな柔軟性を求めて __init__() メソッドに 複数の引数をもたせてもよい。 その場合、クラスのインスタンス生成演算子に与えられた 引数が __init__() へ渡される。 たとえば、

>>> class Complex:
...     def __init__(self, realpart, imagpart):
...         self.r = realpart
...         self.i = imagpart
... 
>>> x = Complex(3.0, -4.5)
>>> x.r, x.i
(3.0, -4.5)

 
9.3.3 インスタンスオブジェクト

ところでインスタンスオブジェクトで何ができるのだろうか? インスタンスオブジェクトが理解する唯一の演算は属性参照だ。 妥当な属性名は2種類ある。

第1の種類をデータ属性 (data attribute) と呼ぶことにする。 これは Smalltalk の ``インスタンス変数'' (instance variable) や C++の ``データメンバ'' (data member) にあたる。 データ属性を宣言する必要はない。 ローカル変数と同じく、これらは最初に代入された時点で突然存在し始める。 たとえば、上記で造った MyClass のインスタンス x に対し、 下記のコード片は、痕跡を残すことなく、値 16 を印字するだろう。

x.counter = 1
while x.counter < 10:
    x.counter = x.counter * 2
print x.counter
del x.counter

インスタンスオブジェクトが理解する第2の種類の属性参照は メソッド (method) だ。 メソッドとは、オブジェクトに ``属している'' 関数 (a function that ``belongs to'' an object) だ。 (Python では、メソッドという用語はクラスインスタンスだけのものではない。 ほかのオブジェクト型にもメソッドはあり得る。 たとえば、リストオブジェクトには append, insert, remove, sort などの メソッドがある。しかし以下では、特に明記しない限り、メソッドという用語を クラスインスタンスオブジェクトのメソッドだけを 意味するものとして使うことにする)

インスタンスオブジェクトの妥当なメソッド名は、そのクラスによって決まる。 定義により、(利用者定義の) 関数オブジェクトであるクラス属性すべてが、 そのインスタンスの対応するメソッドを定義する。 例でいえば、 MyClass.f が関数だから x.f は妥当なメソッド参照となる。しかし、 MyClass.i は関数ではないから x.i は妥当なメソッド参照ではない。 しかし x.fMyClass.f と同じものではない -- それは 関数オブジェクトではなく、  メソッドオブジェクト (method object) だ。

 
9.3.4 メソッドオブジェクト

普通、メソッドはその場ですぐに呼び出される。

x.f()

例では、これは文字列 'hello world' を返すだろう。 しかし、必ずしもメソッドをすぐに呼び出す必要はない。 x.f はメソッドオブジェクトで、 どこかに格納しておいて後から呼び出すこともできる。たとえば、

xf = x.f
while True:
    print xf()

は "hello world" を時の終わりまで印字し続けるだろう。

メソッドが呼び出される時、正確には何が起こるのだろうか? f の関数定義は1個の引数を指定していたのにもかかわらず、 上記で x.f() が無引数で呼び出されたことに気付いているかもしれない。 引数はどうしたのだろうか? たしか、引数が必要な関数を無引数で呼び出すと、Python が例外を ひき起こすはずなのに -- たとえその引数が実際には使われなくても…。

実際、もう答を解き当てているもしれない。 メソッドについて特別なことは、 オブジェクトが関数の第1引数として渡される、ということだ。 例では、呼出し x.f()MyClass.f(x) と正確に等価だ。 一般に、n 引数の並びをもったメソッド呼出しは、対応する関数を、 そのメソッドのオブジェクトを第1引数の前に挿入して造った引数並びとともに 呼び出すことと等価だ。

もしもまだメソッドの働きかたを理解できないなら、 実装を見てみると事情がよく分かるかもしれない。 データ属性ではないインスタンス属性が参照された時は、そのクラスが検索される。 もしもその名前が、関数オブジェクトである妥当なクラス属性を表しているなら、 メソッドオブジェクトが造られる。その方法は、 インスタンスオブジェクトと、今しがた見つけた関数オブジェクト (のそれぞれ を指すポインタ) を、一つの抽象オブジェクトにパック (pack) する、 という方法だ。この抽象オブジェクトがすなわちメソッドオブジェクトだ。 メソッドオブジェクトが引数並びとともに呼び出された時は、 それが再びアンパック (unpack) されて、 インスタンスオブジェクトと元々の引数並びから新しい引数並びが構築される。 そして関数オブジェクトがこの新しい引数並びとともに呼び出される。

 
9.4 いろいろな注意点

[これらはおそらくもっと注意深く配置すべきだろう…]

データ属性は同じ名前のメソッド属性を上書き (override) する。 予期しない名前の衝突は、 大規模なプログラムにおいて見つけにくいバグの原因となり得る。 これを避けるため、衝突の機会を最小にするなんらかの種類の規約 を用いるのが賢明だ。 可能な規約としてメソッド名を大文字で始める、データ属性名に短い一意的な 文字列(あるいはただの下線)を接頭する、またはまたはメソッドには動詞をデー タ属性には名詞を用いるなどがある。

データ属性は、メソッドからだけではなく、 オブジェクトの一般利用者 (``クライアント'') からも同様に参照できる。 言い換えれば、クラスを使って純粋な抽象データ型を実装することはできない。 実際、Python にはデータ隠蔽の強制的な順守を可能にするものは何も ない -- データ隠蔽はすべて規約にもとづいている。 (他方、C で書かれた Python 実装は、実装上の詳細を完全に隠すこと、 そしてもし必要ならばオブジェクトへのアクセスを制御することができる。 C で書かれた Python への拡張はこのことを利用してよい)

クライアントはデータ属性を注意して使うべきだ -- クライアントは、 データ属性を踏みにじることによって、 メソッドが維持している不変式を台無しにすることもできる。 名前の衝突が回避されている限り、クライアントは、 メソッドの妥当性に影響を与えることなく独自のデータ属性を インスタンスオブジェクトに追加してもよいことに 注意しよう -- ここでもやはり、名前付けの規約は頭痛の種をなくすのに役立つ。

メソッド内部からデータ属性を (または、ほかのメソッドを!) 参照するための 短縮記法はない。 私の見るところ、これは実際にはメソッドの可読性を改善している。 メソッドに目を通している時に、 ローカル変数とインスタンス変数を混同する機会は皆無だ。

慣習上、メソッドの第1引数はしばしば self と呼ばれる。 これは単なる規約以外の何ものでもない。すなわち、 self という名前は Python にとって断じてなんら特別な意味はない。 (しかし、この規約に従わなければ、君のコードはほかの Python プログラマに とって読みにくいものになるかもしれない。それに、この規約にもとづいた クラスブラウザ (class browser) プログラムが書かれないとも限らない)

クラス属性である関数オブジェクトはどれも、 そのクラスのインスタンスのためのメソッドを定義する。 関数定義が字面の上でクラス定義に取り囲まれている必要はない。 関数オブジェクトをクラスのローカル変数へ代入するだけでも良い。 たとえば、

# クラスの外側で定義された関数
def f1(self, x, y):
    return min(x, x+y)

class C:
    f = f1
    def g(self):
        return 'hello world'
    h = g

このとき fgh は、 すべて関数オブジェクトを参照するクラス C の属性となり、 したがってすべて C のインスタンスの メソッドとなる -- hg と正確に等価だ。 ただし、 これを実践しても、普通はプログラムの読者を混乱させるのに役立つだけだ。

メソッドは、self 引数のメソッド属性を使うことによって、 ほかのメソッドを呼び出せる。

class Bag:
    def __init__(self):
        self.data = []
    def add(self, x):
        self.data.append(x)
    def addtwice(self, x):
        self.add(x)
        self.add(x)

メソッドは、通常の関数と同じようにグローバル名を参照できる。 メソッドに結合されているグローバルスコープは、 クラス定義を収めているモジュールだ。 (クラスそれ自身がグローバルスコープとして使われることはない!) メソッドでのグローバルデータの利用に賛成する良い理由に出会うことは 滅多にないが、それでもなおグローバルスコープには多くの正当な用途がある。 たとえば、グローバルスコープへ輸入された関数とモジュールが、 同スコープで定義される関数やクラスからはもちろん、 メソッドからも利用可能になる。 普通は、メソッドを収めているクラスもそれ自身 このグローバルスコープで定義される。 次の節では、メソッドが自分のクラスを参照しようとする良い理由を見てみよう!

 
9.5 継承

いうまでもなく、継承 (inheritance) をサポートしないような言語機能は、 ``クラス'' という名前に値しない。 派生クラス (derived class) を定義する構文は次のような形をしている。

class DerivedClassName(BaseClassName):
    <文-1>
    .
    .
    .
    <文-N>

基底クラス (base class) の名前 BaseClassName は、 派生クラス定義を収めているスコープで定義されていなければならない。 基底クラス名のかわりに式でもよい。 これは基底クラスが別モジュールで定義されているとき役に立つ。

class DerivedClassName(modname.BaseClassName):

派生クラス定義の実行は、基底クラスのときと同じように進行する。 クラスオブジェクトが構築される時、基底クラスが記憶される。 これは属性参照を解決するために使われる。すなわち、 もしも要求された属性がクラスに見つからなければ、基底クラスが検索される。 もしも基底クラスそれ自身がほかのクラスから派生しているならば、 この規則が再帰的に適用される。

派生クラスのインスタンス生成について特別なことは何もない -- DerivedClassName() がクラスの新しいインスタンスを造る。 メソッド参照は次のように解決される -- 必要に応じて基底クラスの連鎖を下りつつ、対応するクラス属性が検索され、 そしてその結果が関数オブジェクトならば、そのメソッド参照は妥当だ。

派生クラスは基底クラスのメソッドを上書きしてもよい。 メソッドが同じオブジェクトの別メソッドを呼び出す時に何も特権はないから、 基底クラスのメソッドが同じ基底クラスで定義された別メソッドを呼び出す時、 実際にはそれを上書きした派生クラスのメソッドを呼び出すことになるかもしれない。 (C++ プログラマへ: Python ではすべてのメソッドが 事実上 virtual だ)

派生クラスで上書きしているメソッドは、実際には、 単純に同名の基底クラスメソッドに取って代わりたいのではなく、 むしろそれを拡張したいのかもしれない。 基底クラスメソッドを直接呼び出す簡単な方法がある。 "BaseClassName.methodname(self, 引数)" を呼び出せばよい。 これは時にはクライアントにも役に立つ。 (これが働くのは基底クラスがじかにグローバルスコープで定義 または輸入されているときだけであることに注意しよう)

 
9.5.1 多重継承

Python は限られた形式の多重継承 (multiple inheritance) もサポートしている。 多重の基底クラスを伴ったクラス定義は次のような形をしている。

class DerivedClassName(Base1, Base2, Base3):
    <文-1>
    .
    .
    .
    <文-N>

意味論を説明するために必要な唯一の規則は、 クラス属性参照に使われる解決規則 (resolution rule) だ。 これは深さ優先 (depth-first)、左から右へ (left-to-right) という規則だ。 したがって、もしも属性が DerivedClassName に見つからなければ、 Base1 で検索され、 それから (再帰的に) Base1 の基底クラスで検索される。 そしてそこに見つからないときに限り Base2 で検索される、等々となる。

(人によっては幅優先 (breadth first) -- Base2Base3 を 検索してから Base1 の基底クラスで検索する -- のほうが 自然のように見える。 しかし、もしもそうすると、Base1 のある1属性が実際に Base1 で 定義されているのかそれともその基底クラスのどれかで定義されているのかを 知っていない限り、 君はそれと Base2 の属性との名前衝突がどんな結果をもたらすのか 結論できないことになる。 深さ優先規則は Base1 の直接の属性と継承された属性との差異を皆無にする)

Python は予期しない名前の衝突を規約に頼って回避しているから、 多重継承を見境なく使うと、メンテナンスの悪夢になることは明らかだ。 多重継承にともなう有名な問題に、 たまたま共通の基底クラスをもつ2個のクラスから派生させた1個のクラス、 というものがある。 この場合、何が起こるのかを結論することは簡単だが (インスタンスは、 共通の基底クラスが使用する ``インスタンス変数'' つまりデータ属性を 1組だけもつことになる)、この意味論が何か役に立つかどうかは明らかではない。

 
9.6 プライベート変数

Python にはクラスプライベートな識別子を作成するための限定的な機能がある。 __spam (先頭に2個以上の下線文字、末尾に高々1個の下線文字) という 形式の識別子はどれも今や字面上で _classname__spam へと置換される。 ここで classname は、現在のクラス名から先頭の下線文字を 削除した名前だ。 この符号化加工 (mangling) は、識別子の構文上の位置に関係なく行われるので、 クラスプライベートなインスタンス変数やクラス変数、メソッド、さらには グローバル変数を定義するために利用できるほか、 このクラスにとってプライベートなインスタンス変数を ほかのクラスのインスタンスに格納するためにさえ利用できる。 符号化加工した名前が 255 文字より長くなるときは、 切り詰めが起こるかもしれない。 クラスの外側だったり、クラス名が下線文字だけからできているときは、 符号化加工はされない。

名前の符号化加工は、 派生クラスで定義されるインスタンス変数について心配する必要のない、 あるいはクラスの外側のコードがインスタンス変数をいじりまわすことのない、 そんな ``プライベート'' インスタンス変数およびメソッドを定義する簡便な方法を、 クラスに与えることを意図している。 この符号化加工の規則は主に不慮の事故を避けるために設計されていることに 注意しよう。 プライベートと見なされている変数を参照または改変することは、 もし本当にそうしたければ、依然として可能だ、 このことは、デバッガの中等の特別な状況においてさらに有用になり得る。 そしてそれが、なぜこの抜け穴が閉ざされていないかの一つの理由だ。 (ささいなバグ: 基底クラスと同名のクラスを派生させると、 基底クラスのプライベート変数の使用が可能になる)

execeval()evalfile() へ渡されたコードは、 呼出し元のクラスの classname を現在のクラスだとは見なさないことに注意しよう。 これは global 文の効果と似ている -- その効果もやはり、 一緒にバイトコンパイルされたコードに限定されている。 同じ制約が getattr()setattr()delattr() にも、 もちろん __dict__ をじかに参照するときにも、適用される。

 
9.7 残りのはしばし

ときには Pascal の ``record'' や C の ``struct'' のように、 一組の名前付きデータ項目を一つにまとめるデータ型があると便利だろう。 空のクラス定義はこれをうまく実現する。

class Employee:
    pass

john = Employee() # 空の従業員レコードを造る

# レコードのフィールドを埋める
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000

ある特定の抽象データ型を期待する Python コード片に対し、しばしば、 そのデータ型のメソッドをエミュレートするクラスを代用として渡すことができる。 たとえば、ファイルオブジェクトからデータを読んで書式化する関数に対し、 代用として文字列バッファからデータを得る メソッド read()readline() を備えたクラスを定義して、 それを引数として渡すことができる。

インスタンスメソッドオブジェクトにも属性がある。すなわち、 m.im_self はメソッドのインスタンスオブジェクトで、 m.im_func はメソッドに対応する関数オブジェクトだ。

 
9.8 例外もクラス

利用者定義の例外はクラスによっても識別できる。 このからくりを使えば、拡張可能な例外の階層構造を造ることができる。

raise 文に対し (意味論的に) 新しく妥当になった二つの形式がある。

raise Class, instance

raise instance

第一の形式では、instanceClass またはその派生クラスの インスタンスでなければならない。 第二の形式は次の短縮記法だ。

raise instance.__class__, instance

except 節のクラスが例外と適合するのは、 例外がそれと同じクラスまたはその派生クラスの場合だ (しかし逆方向には 適合しない -- 派生クラスを並べた except 節は基底クラスとは適合しない)。 たとえば、次のコードは B, C, D をこの順序で印字する。

class B:
    pass
class C(B):
    pass
class D(C):
    pass

for c in [B, C, D]:
    try:
        raise c()
    except D:
        print "D"
    except C:
        print "C"
    except B:
        print "B"

もしも except 節が逆に並んでいたら ("except B" が最初だったら)、 B, B, B と印字されたはずだったことに注意しよう -- 最初に マッチした except 節が駆動される。

処理されない例外に対してエラーメッセージが印字される時、 もしその例外がクラスならば、クラス名に続けてコロンとスペースが印字され、 そして最後に、組込み関数 str() を使って文字列へと変換された インスタンスが印字される。

 
9.9 イテレーター

ほとんどのコンテナオブジェクトは for ステートメントを使用してルー プさせる事できる。

for element in [1, 2, 3]:
    print element
for element in (1, 2, 3):
    print element
for key in {'one':1, 'two':2}:
    print key
for char in "123":
    print char
for line in open("myfile.txt"):
    print line

この方法は明確で、簡潔で、便利だ。イテレーターの使用は Python に浸透し 統一されている。for 文は内部でコンテナオブジェクトの iter() を利用している。 この関数は、一度でコンテナーオブジェクト中の要素にアクセスする。メソッ ド next() を定義したイテレーターオブジェクトを返す。 要素がこれ以上ない場合、メソッド next()StopIteration 例外が発生し for が終了する。 以下の例でこれがどのように動作するかを示す。

>>> s = 'abc'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> it.next()
'a'
>>> it.next()
'b'
>>> it.next()
'c'
>>> it.next()

Traceback (most recent call last):
  File "<pyshell#6>", line 1, in -toplevel-
    it.next()
StopIteration

イテレーターの背後にある仕組みを見て、独自クラスにイテレーターの振る舞 いを加えることは容易だ。 next() メソッドを備えたオブジェクトを返す __iter__() メソッドを定義する。 クラスに next() を定義すれば、 __iter__()self をちょうど返すことができる。

>>> class Reverse:
    "Iterator for looping over a sequence backwards"
    def __init__(self, data):
        self.data = data
        self.index = len(data)
    def __iter__(self):
        return self
    def next(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]

>>> for char in Reverse('spam'):
    print char

m
a
p
s

 
9.10 ジェネレーター

ジェネレーターは、イテレーターを作成するための単純で強力なツールだ。そ れらは正則関数のように書かれているが、データを返したい場合は常に、 yield ステートメントを使用する。next() を呼ぶたびに 積み残し(データの値およびステートメントが最後にどれが実行されたかすべ て思い出す)からジェネレーターは再開する。以下の例では、ジェネレーター を作成することが取るに足りないほど容易かもしれないことを示す。

>>> def reverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index]
        
>>> for char in reverse('golf'):
    print char

f
l
o
g

どんなジェネレーターにできるものはすべて、前章で記述したようなクラスに 基づいたイテレーターにできる。 __iter__()next() メソッドが自動的に作成される事で ジェネレーターは非常にコンパクトになっている。

別の重要な特徴は、ローカル変数および実行状態が呼び出しの間で自動的に保 存されるということだ。 これは、関数を書くことをより容易にし、self.indexself.data の様なクラス変数を使用するアプローチをより明瞭にする。

自動メソッド生成および状態の保持に加えて、ジェネレーターが終了する時自 動的に StopIteration が発生する。 これらの特徴によい、正則関数を書くよりは多くの努力を必要とせず容易にイ テレーターを作成することができる。



... すなわち、それらは同じ名前空間を共有している!9.1
一つ例外がある。 モジュールオブジェクトには、モジュールの名前空間を実装する ために使われている辞書を返す秘密の読取り専用属性 __dict__ が ある。__dict__ という名前は属性だがグローバル名ではない。 明らかに、これの利用は、名前空間の実装の抽象化を侵していて、 したがって検死デバッガのようなものに限られるべきだ。
See About this document... for information on suggesting changes.