9. クラス

クラスはデータと機能を組み合わせる方法を提供します。 新規にクラスを作成することで、新しいオブジェクトの 型 を作成し、その型を持つ新しい インスタンス が作れます。 クラスのそれぞれのインスタンスは自身の状態を保持する属性を持てます。 クラスのインスタンスは、その状態を変更するための (そのクラスが定義する) メソッドも持てます。

Python のクラス機構は、ほかのプログラミング言語に比べて、新しく覚える構文や規則を最小限に抑えています。 その仕組みは C++ と Modula-3 のクラス機構を組み合わせたものです。 Python のクラスは、オブジェクト指向プログラミングの標準的な機能をすべて備えています。 複数の基底クラスを継承でき、派生クラスでは基底クラスのどのメソッドもオーバーライドできます。 また、メソッドから基底クラスの同名のメソッドを呼び出せます。 オブジェクトに格納するデータの種類や数も自由です。 モジュールと同様に、クラスも Python の動的な性質を持っています。 クラスは実行時に作られ、作成後に変更することもできます。

C++ の用語で言うと、クラスメンバはデータメンバも含めて通常 public です (例外については プライベート変数 を参照してください)。 メンバ関数はすべて 仮想関数 (virtual) です。 Modula-3 と同様に、メソッド内からオブジェクトのメンバを参照するための短縮記法はありません。 メソッド関数の宣言では、オブジェクト自体を表す引数を最初に明示します。 メソッドを呼び出すと、この引数にオブジェクトが暗黙的に渡されます。 Smalltalk と同様に、クラス自体もオブジェクトなので、インポートしたり別の名前を付けたりできます。 C++ や Modula-3 と違って、組み込み型を基底クラスにして拡張することもできます。 また、C++ と同様に、算術演算子や添字表記など特別な構文を使う組み込み演算子の多くは、 クラスインスタンスに対する動作を再定義できます。

(クラスに関して普遍的な用語定義がないので、 Smalltalk と C++ の用語を場合に応じて使っていくことにします。 C++ よりも Modula-3 の方がオブジェクト指向の意味論が Python に近いので、 Modula-3 の用語を使いたいのですが、ほとんどの読者は Modula-3 についてしらないでしょうから。)

9.1. 名前とオブジェクトについて

オブジェクトはそれぞれ固有の存在ですが、同じオブジェクトに複数のスコープから複数の名前を束縛できます。 ほかの言語では、これをエイリアス (別名) と呼びます。 Python を学び始めたばかりでは、この仕組みの重要性は分かりにくいかもしれません。 数値、文字列、タプルのようなイミュータブル (変更できない) な基本型を扱うときは、気にしなくても問題ありません。 一方、リストや辞書など多くの型のミュータブル (変更できる) なオブジェクトを扱うコードでは、 エイリアスによって意外な動作が起こることがあります。 エイリアスはいくつかの点でポインタのように振る舞うため、通常はその性質をプログラムに役立てられます。 例えば、オブジェクトを渡す際に実装上渡されるのはポインタだけなので、処理の負担は小さくなります。 また、関数が引数として受け取ったオブジェクトを変更すると、呼び出し側からもその変更が見えます。 このため、Pascal のように引数の渡し方を 2 種類用意する必要がありません。

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

クラスを紹介する前に、Python のスコープの規則について説明する必要があります。 クラス定義は名前空間を巧みに利用するので、その動作を十分に理解するには、スコープと名前空間の仕組みを知っておく必要があります。 なお、この知識は高度なプログラミングを行う Python プログラマーなら誰にとっても役立ちます。

まず定義から始めましょう。

名前空間 (namespace) とは、名前からオブジェクトへの対応付け (mapping) です。ほとんどの名前空間は、現状では Python の辞書として実装されていますが、そのことは通常は (パフォーマンス以外では) 目立つことはないし、将来は変更されるかもしれません。名前空間の例には、組込み名の集合 (abs() 等の関数や組込み例外名)、モジュール内のグローバルな名前、関数を呼び出したときのローカルな名前があります。オブジェクトの属性からなる集合もまた、ある意味では名前空間です。名前空間について知っておくべき重要なことは、異なった名前空間にある名前の間には全く関係がないということです。例えば、二つの別々のモジュールの両方で関数 maximize という関数を定義することができ、定義自体は混同されることはありません --- モジュールのユーザは名前の前にモジュール名をつけなければなりません。

ところで、 属性 という言葉は、ドットに続く名前すべてに対して使っています --- 例えば式 z.real で、 real はオブジェクト z の属性です。厳密にいえば、モジュール内の名前に対する参照は属性の参照です。式 modname.funcname では、 modname はあるモジュールオブジェクトで、 funcname はその属性です。この場合には、モジュールの属性とモジュールの中で定義されているグローバル名の間には、直接的な対応付けがされます。これらの名前は同じ名前空間を共有しているのです! [1]

属性は読取り専用にも、書込み可能にもできます。書込み可能であれば、属性に代入することができます。モジュール属性は書込み可能で、 modname.the_answer = 42 と書くことができます。書込み可能な属性は、 del 文で削除することもできます。例えば、 del modname.the_answer は、 modname で指定されたオブジェクトから属性 the_answer を除去します。

名前空間は様々な時点で作成され、その寿命も様々です。組み込みの名前が入った名前空間は Python インタプリタが起動するときに作成され、決して削除されることはありません。モジュールのグローバルな名前空間は、モジュール定義が読み込まれたときに作成されます。通常、モジュールの名前空間は、インタプリタが終了するまで残ります。インタプリタのトップレベルで実行された文は、スクリプトファイルから読み出されたものでも対話的に読み出されたものでも、 __main__ という名前のモジュールの一部分であるとみなされるので、独自の名前空間を持つことになります。 (組み込みの名前は実際にはモジュール内に存在します。そのモジュールは builtins と呼ばれています。)

関数のローカルな名前空間は、関数が呼び出されたときに作成され、関数から戻ったときや、関数内で例外が送出され、かつ関数内で処理されなかった場合に削除されます。 (実際には、忘れられる、と言ったほうが起きていることをよく表しています。) もちろん、再帰呼出しのときには、各々の呼び出しで各自のローカルな名前空間があります。

スコープ (scope) とは、ある名前空間が直接アクセスできるような、 Python プログラムのテキスト上の領域です。 "直接アクセス可能" とは、修飾なしに (訳注: spam.egg ではなく単に egg のように) 名前を参照した際に、その名前空間から名前を見つけようと試みることを意味します。

スコープは静的に決定されますが、動的に使用されます。実行中はいつでも、直接名前空間にアクセス可能な、3つまたは4つの入れ子になったスコープがあります:

  • 最初に探される、最も内側のスコープは、ローカルな名前を持っています。

  • 外側の(enclosing)関数のスコープは、近いほうから順に探され、ローカルでもグローバルでもない名前を持っています。

  • 最後から 2 番目のスコープは現在のモジュールのグローバルな名前を持っています。

  • 一番外側の(最後に検索される)スコープはビルトイン名を持っています。

名前を global と宣言すると、その名前への参照と代入はすべて、モジュールのグローバル名を持つ最後から 2 番目のスコープで直接行われます。 最も内側のスコープより外側にある変数を再束縛するには、nonlocal 文を使えます。 こうした外側の変数は、nonlocal と宣言しない限り読み出し専用です。 代入しようとすると、最も内側のスコープに同名の 新しい ローカル変数が作られ、外側の変数は変わりません。

通常、ローカルスコープは (プログラムテキスト上の) 現在の関数のローカルな名前を参照します。関数の外側では、ローカルスコープはグローバルな名前空間と同じ名前空間、モジュールの名前空間を参照します。クラス定義では、ローカルスコープの中にもう一つ名前空間が置かれます。

スコープはテキスト上で決定されていると理解することが重要です。モジュール内で定義される関数のグローバルなスコープは、関数がどこから呼び出されても、どんな別名をつけて呼び出されても、そのモジュールの名前空間になります。反対に、実際の名前の検索は実行時に動的に行われます --- とはいえ、言語の定義は、"コンパイル" 時の静的な名前解決の方向に進化しているので、動的な名前解決に頼ってはいけません! (事実、ローカルな変数は既に静的に決定されています。)

global 文や nonlocal 文が有効でない場合、 Python では名前への代入は常に最も内側のスコープで行われます。 代入はデータをコピーせず、名前をオブジェクトに束縛するだけです。 削除も同様で、del x はローカルスコープの名前空間から x の束縛を削除します。 新しい名前を導入する操作はすべてローカルスコープを使います。 例えば import 文や関数定義は、モジュール名や関数名をローカルスコープで束縛します。

global 文を使うと、特定の変数がグローバルスコープに存在し、そこで再束縛されることを指示できます。 nonlocal 文は、特定の変数が外側のスコープに存在し、そこで再束縛されることを指示します。

9.2.1. スコープと名前空間の例

異なるスコープと名前空間がどのように参照されるか、また global および nonlocal が変数の束縛にどう影響するか、この例で実演します:

def scope_test():
    def do_local():
        spam = "local spam"

    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal spam"

    def do_global():
        global spam
        spam = "global spam"

    spam = "test spam"
    do_local()
    print("After local assignment:", spam)
    do_nonlocal()
    print("After nonlocal assignment:", spam)
    do_global()
    print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)

このコード例の出力は:

After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam

このとおり、(デフォルトの) ローカルな 代入は scope_test 上の spam への束縛を変更しませんでした。 nonlocal 代入は scope_test 上の spam への束縛を変更し、 global 代入はモジュールレベルの束縛を変更しました。

また、global による代入の前には、グローバルスコープで spam がまだ束縛されていなかったことも分かります。

9.3. クラス初見

クラスでは、新しい構文を少しと、三つの新たなオブジェクト型、そして新たな意味付けをいくつか取り入れています。

9.3.1. クラス定義の構文

クラス定義の最も単純な形式は、次のようになります:

class ClassName:
    <statement-1>
    .
    .
    .
    <statement-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.i と MyClass.f は有効な属性参照であり、それぞれ整数と関数オブジェクトを返します。クラス属性も代入できるため、代入により MyClass.i の値を変えられます。 __doc__ も有効な属性で、そのクラスに属しているドキュメンテーション文字列(docstring) 、この場合は "A simple example class" を返します。

クラスの インスタンス化 (instantiation) には関数記法を使います。クラスオブジェクトのことを、クラスの新しいインスタンスを返す、引数のない関数のように扱ってください。上記クラスで例示すると:

x = MyClass()

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

インスタンス化操作 (クラスオブジェクトの "呼出し") では、空のオブジェクトが作られます。多くのクラスでは、特定の初期状態にカスタマイズされたオブジェクトを作りたいです。そのために、クラスには __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. インスタンスオブジェクト

ところで、インスタンスオブジェクトを使うと何ができるのでしょうか?インスタンスオブジェクトが理解できる唯一の操作は、属性の参照です。有効な属性名には (データ属性およびメソッドの) 二種類あります。

データ属性 (data attribute) は Smalltalk の「インスタンス変数」や C++ の「データメンバ」に相当します。 データ属性を宣言する必要はありません。 ローカル変数と同様に、初めて値を代入したときに作られます。 例えば、x が上で作成した MyClass のインスタンスなら、 次のコードは値 16 を表示した後、追加した counter 属性を削除します。

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

もう一つのインスタンス属性の参照は、 メソッド です。メソッドとは、オブジェクトに "属する" 関数のことです。

インスタンスオブジェクトで有効なメソッド名は、そのクラスによって決まります。 定義上、クラス属性が関数オブジェクトであれば、そのクラスのインスタンスに対応するメソッドが定義されます。 この例では MyClass.f が関数なので、x.f は有効なメソッド参照です。 一方、MyClass.i は関数ではないので、x.i はメソッド参照ではありません。 ただし、x.f は MyClass.f と同じものではありません。 x.f は関数オブジェクトではなく、メソッドオブジェクト (method object) です。

9.3.4. メソッドオブジェクト

普通、メソッドはバインドされた直後に呼び出されます:

x.f()

もし x = MyClass() であれば、上記のコードは文字列 'hello world' を返すでしょう。しかしながら、必ずしもメソッドをその場で呼び出さなければならないわけではありません。 x.f はメソッドオブジェクトであり、どこかに記憶しておいて後で呼び出すことができます。例えば次のコードは:

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

hello world を時が終わるまで印字し続けるでしょう。

メソッドが呼び出されるときには実際には何が起きているのでしょうか? f() の関数定義では引数を一つ指定していたにもかかわらず、上の例では x.f() が引数なしで呼び出されています。引数はどうなったのでしょうか?たしか、引数が必要な関数を引数無しで呼び出すと、 Python が例外を送出するはずです --- たとえその引数が実際には使われなくても…。

もう答は想像できているかもしれませんね: メソッドについて特別なこととして、インスタンスオブジェクトが関数の第1引数として渡されます。 例では、 x.f() という呼び出しは、 MyClass.f(x) と厳密に等価なものです。 一般に、 n 個の引数リストもったメソッドの呼出しは、そのメソッドのインスタンスオブジェクトを最初の引数の前に挿入した引数リストで、メソッドに対応する関数を呼び出すことと等価です。

メソッドは一般に次のように動作します。 インスタンスの非データ属性を参照すると、そのインスタンスのクラスから属性を探します。 見つかった属性が関数オブジェクトなら、インスタンスと関数の両方への参照をまとめてメソッドオブジェクトにします。 そのメソッドを呼び出すと、渡された引数の先頭にインスタンスを加えた新しい引数リストが作られます。 そして、その引数リストで関数を呼び出します。

9.3.5. クラス変数とインスタンス変数

一般的に、インスタンス変数はそれぞれのインスタンスについて固有のデータのためのもので、クラス変数はそのクラスのすべてのインスタンスによって共有される属性やメソッドのためのものです:

class Dog:

    kind = 'canine'         # 全インスタンスで共有されるクラス変数

    def __init__(self, name):
        self.name = name    # インスタンスごとに固有のインスタンス変数

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind                  # すべての犬で共有
'canine'
>>> e.kind                  # すべての犬で共有
'canine'
>>> d.name                  # d 固有
'Fido'
>>> e.name                  # e 固有
'Buddy'

名前とオブジェクトについて で述べたように、共有データはリストや辞書のような mutable オブジェクトで予期せぬ作用を生じることがあります。例えば、以下のコードの tricks リストはクラス変数として使われるべきではありません、なぜなら単一のリストがすべての Dog インスタンスで共有されてしまうからです:

class Dog:

    tricks = []             # クラス変数の間違った使用

    def __init__(self, name):
        self.name = name

    def add_trick(self, trick):
        self.tricks.append(trick)

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks                # 意図せず すべての犬で共有
['roll over', 'play dead']

このクラスの正しい設計ではインスタンス変数を代わりに使用するべきです:

class Dog:

    def __init__(self, name):
        self.name = name
        self.tricks = []    # 犬ごとに新しい空リストを作る

    def add_trick(self, trick):
        self.tricks.append(trick)

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks
['roll over']
>>> e.tricks
['play dead']

9.4. いろいろな注意点

インスタンスとクラスに同じ名前の属性がある場合、属性の検索ではインスタンス側が優先されます。

>>> class Warehouse:
...    purpose = 'storage'
...    region = 'west'
...
>>> w1 = Warehouse()
>>> print(w1.purpose, w1.region)
storage west
>>> w2 = Warehouse()
>>> w2.region = 'east'
>>> print(w2.purpose, w2.region)
storage east

データ属性は、メソッドからも、オブジェクトの通常の利用者 (「クライアント」) からも参照できます。 つまり、クラスを使って純粋な抽象データ型を実装することはできません。 実際、Python にはデータの隠蔽を強制する仕組みがなく、すべて規約に基づいています。 (一方、C で書かれた Python の実装では、必要に応じて実装の詳細を完全に隠し、オブジェクトへのアクセスを制御できます。 C で書く Python の拡張ではこの仕組みを使えます。)

クライアントはデータ属性を注意して使うべきです。 データ属性を書き換えると、メソッドが維持する不変条件を壊す可能性があります。 ただし、名前の衝突を避ければ、クライアントはメソッドの正常な動作に影響を与えずに、インスタンスオブジェクトへ独自のデータ属性を追加できます。 ここでも、名前付けの規約を使うと多くの問題を避けられます。

メソッドの中から、データ属性を (または別のメソッドも!) 参照するための短縮された記法はありません。私は、この仕様がメソッドの可読性を高めていると感じています。あるメソッドを眺めているときにローカルな変数とインスタンス変数をはっきり区別できるからです。

メソッドの最初の引数は self と呼ぶことが多いです。 これは単なる慣習であり、self という名前自体は Python にとって特別な意味を持ちません。 ただし、この慣習に従わないと、他の Python プログラマーにとってコードが読みにくくなる可能性があります。 また、この慣習に依存する クラスブラウザ (class browser) プログラムが書かれることも考えられます。

クラス属性である関数オブジェクトはいずれも、そのクラスのインスタンスのためのメソッドを定義しています。関数定義は、テキスト上でクラス定義の中に入っている必要はありません。関数オブジェクトをクラスのローカルな変数の中に代入するのも OK です。例えば以下のコードのようにします:

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

class C:
    f = f1

    def g(self):
        return 'hello world'

    h = g

これで、 f 、 g 、および h は、すべて C の属性であり関数オブジェクトを参照しています。従って、これらは、すべて C のインスタンスのメソッドとなります --- h は g と全く等価です。これを実践しても、大抵は単にプログラムの読者に混乱をもたらすだけなので注意してください。

メソッドは、 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)

メソッドは通常の関数と同様にグローバル名を参照できます。 メソッドのグローバルスコープは、その定義を含むモジュールです (クラスがグローバルスコープになることはありません)。 メソッドでグローバルデータを使う適切な理由はまれですが、グローバルスコープには妥当な用途が数多くあります。 例えば、グローバルスコープにインポートした関数やモジュール、そのスコープで定義した関数やクラスは、メソッドからも使えます。 通常、そのメソッドを含むクラス自体もこのグローバルスコープで定義されています。 次の節では、メソッドが自身のクラスを参照したくなる理由を見ていきます。

個々の値はオブジェクトなので、 クラス (型 とも言います) を持っています。それは object.__class__ に保持されています。

9.5. 継承

言うまでもなく、継承の概念をサポートしない言語機能は "クラス" と呼ぶに値しません。派生クラス (derived class) を定義する構文は次のようになります:

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

基底クラス (base class) の名前 BaseClassName は、派生クラス定義の入っているスコープからアクセスできる名前空間で定義されていなければなりません。基底クラス名のかわりに任意の式を入れることもできます。これは次の例のように、基底クラスが別モジュールで定義されているときに便利なことがあります:

class DerivedClassName(modname.BaseClassName):

派生クラス定義の実行は、基底クラスの場合と同じように進められます。クラスオブジェクトが構築される時、基底クラスが記憶されます。記憶された基底クラスは、属性参照を解決するために使われます。要求された属性がクラスに見つからなかった場合、基底クラスに検索が進みます。この規則は、基底クラスが他の何らかのクラスから派生したものであった場合、再帰的に適用されます。

派生クラスのインスタンス化では、特別なことは何もありません。 DerivedClassName() はクラスの新たなインスタンスを生成します。メソッドの参照は次のようにして解決されます。まず対応するクラス属性が検索されます。検索は、必要に応じ、基底クラス連鎖を下って行われ、検索の結果として何らかの関数オブジェクトがもたらされた場合、メソッド参照は有効なものとなります。

派生クラスは基底クラスのメソッドを上書き (override) することができます。メソッドは同じオブジェクトの別のメソッドを呼び出す際に何ら特殊な権限を持ちません。このため、ある基底クラスのメソッドが、同じ基底クラスで定義されているもう一つのメソッド呼び出しを行っている場合、派生クラスで上書きされた何らかのメソッドが呼び出されることになるかもしれません。 (C++ プログラマへ: Python では、すべてのメソッドは事実上 virtual です。)

派生クラスで上書きしているメソッドでは、基底クラスの同名のメソッドを置き換えるのではなく、拡張したいのかもしれません。基底クラスのメソッドを直接呼び出す簡単な方法があります。単に BaseClassName.methodname(self, arguments) を呼び出すだけです。この仕様は、場合によってはクライアントでも役に立ちます。 (この呼び出し方が動作するのは、基底クラスがグローバルスコープの BaseClassName という名前でアクセスできるときだけです。)

Python には継承に関係する 2 つの組み込み関数があります:

  • isinstance() を使うとインスタンスの型が調べられます。 isinstance(obj, int) は obj.__class__ が int や int の派生クラスの場合に限り True になります。

  • issubclass() を使うとクラスの継承関係が調べられます。 bool は int のサブクラスなので issubclass(bool, int) は True です。しかし、 float は int のサブクラスではないので issubclass(float, int) は False です。

9.5.1. 多重継承

Python では、多重継承 (multiple inheritance) の形式もサポートしています。複数の基底クラスをもつクラス定義は次のようになります:

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

ほとんどのシンプルな多重継承において、親クラスから継承される属性の検索は、深さ優先で、左から右に、そして継承の階層の中で同じクラスが複数出てくる(訳注: ダイアモンド継承と呼ばれます)場合に2度探索をしない、と考えることができます。なので、ある属性が DerivedClassName で見つからない場合、まず Base1 から検索され、そして(再帰的に) Base1 の基底クラスから検索され、それでも見つからなかった場合は Base2 から検索される、といった具合になります。

実際には、それよりもう少しだけ複雑です。協調的な super() の呼び出しのためにメソッドの解決順序は動的に変更されます。このアプローチは他の多重継承のある言語で call-next-method として知られており、単一継承しかない言語の super 呼び出しよりも強力です。

検索順序を動的に決める必要があるのは、どの多重継承にも 1 つ以上のダイヤモンド関係があるためです。 これは、最も下位のクラスから同じ祖先クラスに複数の経路でたどり着ける関係を指します。 例えば、すべてのクラスは object を継承するので、多重継承では object に至る経路が複数あります。 同じ基底クラスを何度もたどらないように、動的なアルゴリズムで検索順序を一列に並べます。 この順序は、各クラスで指定した基底クラスの左から右への順序を保ち、各祖先クラスを一度だけたどるように決まります。 さらに、サブクラスを作っても祖先クラス同士の優先順位が変わらない「単調性」を保ちます。 こうした性質により、信頼性が高く拡張しやすい多重継承のクラスを設計できます。 詳しくは Python 2.3 のメソッド解決順序 を参照してください。

いくつかの場合では多重継承が許されません。詳細は 多重継承 を見てください。

9.6. プライベート変数

オブジェクトの中からしかアクセス出来ない "プライベート" インスタンス変数は、 Python にはありません。しかし、ほとんどの Python コードが従っている慣習があります。アンダースコアで始まる名前 (例えば _spam) は、 (関数であれメソッドであれデータメンバであれ) 非 public なAPIとして扱います。これらは、予告なく変更されるかもしれない実装の詳細として扱われるべきです。

クラスのプライベートメンバは、サブクラスで定義された名前との衝突を避けるのに役立ちます。 この用途のために、Python は名前マングリング (name mangling) という限定的な仕組みを備えています。 __spam のように、先頭に 2 個以上、末尾に 1 個以下のアンダースコアを持つ識別子は、 テキスト上で _classname__spam に置き換えられます。 classname は現在のクラス名から先頭のアンダースコアを取り除いたものです。 対象の識別子がクラス定義内にあれば、構文上の位置に関係なくこの変換が行われます。

参考

詳細と特例は、 private name mangling specifications 。

名前マングリングは、サブクラスが内部のメソッド呼び出しを壊さずにメソッドをオーバーライドするのに便利です。例えば:

class Mapping:
    def __init__(self, iterable):
        self.items_list = []
        self.__update(iterable)

    def update(self, iterable):
        for item in iterable:
            self.items_list.append(item)

    __update = update   # 元の update() メソッドのプライベートコピー

class MappingSubclass(Mapping):

    def update(self, keys, values):
        # update() への新シグネチャ導入
        # しかし __init__() は壊さない
        for item in zip(keys, values):
            self.items_list.append(item)

上の例は、もし仮に MappingSubclass に __update 識別子を実装したとしてもきちんと動きます。 その理由は、 Mapping クラスではその識別子を _Mapping__update に、 MappingSubclass クラスでは _MappingSubclass__update にそれぞれ置き換えるからです。

名前マングリングの主な目的は、名前の衝突などの事故を防ぐことです。 プライベートとされている変数にも、アクセスしたり値を変更したりすることはできます。 デバッガなどでは、この性質が役立つこともあります。

exec() や eval() に渡したコードでは、呼び出し元のクラスは名前マングリングに使う現在のクラスとして扱われません。 これは global 文と同様で、効果が及ぶのは一緒にバイトコンパイルされるコードの範囲だけです。 getattr()、setattr()、delattr() を使う場合や、__dict__ を直接参照する場合も同じ制約があります。

9.7. 残りのはしばし

Pascal のレコードや C の構造体のように、名前付きのデータ項目をまとめる型があると便利なことがあります。 Python では、この用途に dataclasses を使うのが一般的です。

from dataclasses import dataclass

@dataclass
class Employee:
    name: str
    dept: str
    salary: int
>>> john = Employee('john', 'computer lab', 1000)
>>> john.dept
'computer lab'
>>> john.salary
1000

特定の抽象データ型を想定した Python コードには、その型と同じように動くメソッドを備えたクラスを代わりに使えることがよくあります。 例えば、ファイルオブジェクトから読み取ったデータをフォーマットする関数があるとします。 ファイルの代わりに文字列バッファからデータを読み取る read() と readline() メソッドを持つクラスを定義すれば、 そのインスタンスをこの関数の引数として渡せます。

Instance method objects にも属性があります。 m.__self__ はメソッド m() の属しているインスタンスオブジェクトで、m.__func__ はそのメソッドに対応する function object です。

9.8. イテレータ (iterator)

すでに気づいているでしょうが、 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, end='')

こういう要素へのアクセス方法は明確で簡潔で使い易いものです。イテレータの活用は Python へ広く行き渡り、統一感を持たせています。裏では for 文はコンテナオブジェクトに対して iter() 関数を呼んでいます。関数は、コンテナの中の要素に1つずつアクセスする __next__() メソッドが定義されているイテレータオブジェクトを返します。これ以上要素が無い場合は、 __next__() メソッドは StopIteration 例外を送出し、その通知を受け for ループは終了します。組み込みの next() 関数を使って __next__() メソッドを直接呼ぶこともできます; この例は関数がどう働くのかを示しています:

>>> s = 'abc'
>>> it = iter(s)
>>> it
<str_iterator object at 0x10c90e650>
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    next(it)
StopIteration

イテレータプロトコルの裏にある仕組みを観察していれば、自作のクラスにイテレータとしての振舞いを追加するのは簡単です。 __next__() メソッドを持つオブジェクトを返す __iter__() メソッドを定義するのです。クラスが __next__() メソッドを定義している場合、 __iter__() メソッドは単に self を返すことも可能です:

class Reverse:
    """シーケンスを後ろからまわるイテレータ."""
    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]
>>> rev = Reverse('spam')
>>> iter(rev)
<__main__.Reverse object at 0x00A1DB50>
>>> for char in rev:
...     print(char)
...
m
a
p
s

9.9. ジェネレータ (generator)

ジェネレータ は、イテレータを作成するための簡潔で強力なツールです。ジェネレータは通常の関数のように書かれますが、何らかのデータを返すときには 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.index や self.data のようなインスタンス変数を使う方法よりも、関数を簡単に書け、コードもずっと明確になります。

メソッドを自動生成したりプログラムの実行状態を自動保存するほかに、ジェネレータは終了時に自動的に StopIteration を送出します。これらの機能を組み合わせると、通常の関数を書くのと同じ労力で、簡単にイテレータを生成できます。

9.10. ジェネレータ式

単純なジェネレータは、リスト内包表記の角括弧を丸括弧に替えたような構文で、短い式として書けます。 このジェネレータ式は、ジェネレータを関数の引数として渡し、その関数ですぐに使う場合に適しています。 ジェネレータを関数として定義するより用途は限られますが、短く書けます。 また、同じ内容のリスト内包表記よりメモリ使用量を抑えられることが多くなります。

例:

>>> sum(i*i for i in range(10))                 # 平方和
285

>>> xvec = [10, 20, 30]
>>> yvec = [7, 5, 3]
>>> sum(x*y for x,y in zip(xvec, yvec))         # ドット積
260

>>> unique_words = set(word for line in page  for word in line.split())

>>> valedictorian = max((student.gpa, student.name) for student in graduates)

>>> data = 'golf'
>>> list(data[i] for i in range(len(data)-1, -1, -1))
['f', 'l', 'o', 'g']

>>> x = [[1,2,3], [], [4, 5]]
>>> g = (*i for i in x)
>>> list(g)
[1, 2, 3, 4, 5]

多くの場合、ジェネレータ式は括弧で囲む必要があります。 ただし特例として、唯一の実引数として関数に渡す場合(前述の sum, set, max, list の例のように)は、ジェネレータ式を追加の括弧で囲む必要がありません。 すなわち、次の2つのコード片は意味的に等価です:

>>> f(x for x in y)
>>> f((x for x in y))

以下も同様です:

>>> f(*x for x in y)
>>> f((*x for x in y))

脚注