Commits

Naoki INADA committed 91c550b

class を追加

Comments (0)

Files changed (2)

+クラス
+======
+
+定義
+----
+
+Python のクラスを書くには、 ``class`` 文を使います。
+
+::
+
+    >>> class Foo(object):
+    ...     def __init__(self, a):
+    ...         self.a = a
+    ...     def double(self):
+    ...         return self.a * 2
+    ...
+    >>> foo = Foo(3)
+    >>> foo.a
+    3
+    >>> foo.double()
+    6
+
+php の ``__construct`` にあたる関数が ``__init__`` です。
+属性へのアクセスには ``->`` ではなく ``.`` を使います。
+
+php の ``$this->`` に当たるのが、 ``self.`` です。
+php と違って、 ``self`` は明示的に第一引数として受け取らないといけません。
+これは、 class 文の中でメソッドじゃないただの関数を定義できるので、 self を受け取る関数か
+そうじゃない関数かを構文で区別しようとすると複雑になってしまうからです。
+
+関数と同じく、クラスもファーストクラスオブジェクトです。
+上の例では、 class 文は ``Foo`` という変数にクラスオブジェクトを代入しています。
+クラスは関数と同じく ``()`` をつけて呼び出すことができ、クラスを呼び出すとインスタンスができます。
+なので、 ``new`` に当たる構文はありません。
+
+クラス属性とインスタンス属性
+------------------------------
+
+php でいうメンバー変数を、 Python では属性と呼びます。
+
+クラスの属性 (phpで言うstaticメンバー変数) とインスタンスの属性 (phpで言うメンバー変数)
+の違いに気をつけてください。
+Python で class 定義内で変数を定義すると、それは自動的にクラスの属性になります。
+インスタンスの属性を定義するには ``__init__`` などで代入してやる必要があります。
+
+オブジェクトの属性を ``a = x.y`` の用に参照する場合、まず ``x`` 自体の属性から ``y`` を探して、
+なかったら ``x`` のクラスの属性を探します。
+
+
+::
+
+    >>> class Foo(object):
+    ...     x = 1   # Foo の属性
+    ...     def __init__(self, a):
+    ...         self.a = a   # self の属性
+    ...
+    >>> foo = Foo(3)
+    >>> foo.a   # foo の属性 a を参照する
+    3
+    >>> foo.x   # foo には x が無いので、 Foo.x を参照する
+    1
+    >>> foo.x = 5  # foo.x に代入したので、 foo.x ができる
+    >>> foo.x   # foo の属性 x を参照する
+    5
+    >>> Foo.x   # Foo の属性 x はそのまま
+    1
+    >>> Foo.z = 'zzz'   # 後から Foo に属性を追加
+    >>> foo.z   # これも foo から参照できる
+    'zzz'
+
+この特徴を活かして、クラス属性をインスタンス属性の初期値の用にして使うことができます。
+ただし、クラス属性はどのインスタンスからアクセスしても同一のオブジェクトになるので、
+関数の引数のデフォルト値と同じような問題に注意してください。
+
+::
+
+    >>> class Bar(object):
+    ...     x = []
+    ...     def __init__(self):
+    ...         self.x.append(1)
+    ...
+    >>> bar = Bar()
+    >>> bar = Bar()
+    >>> Bar.x
+    [1, 1]
+
+Python では関数も変数に入るただのオブジェクトだという話をしましたが、
+メソッドの正体はクラスの属性に代入された関数オブジェクトです。
+
+ただし、関数にはちょっとしたマジックがあって、インスタンスから属性アクセスされた時に
+第一引数にそのインスタンスを束縛した、メソッドオブジェクトを返すのです。
+
+このマジックはメソッドを作るためだけの仕組みじゃないのですが、
+マジックの使い方は高度な内容になるのでここでは割愛します。
+
+
+プロパティ
+----------
+
+php では ``$x->setFoo($y)`` や ``$z = $x->getFoo()`` といったアクセッサを定義することが多いですが、
+Python では ``x.foo = y`` や ``z = x.foo`` と書くだけでアクセッサ関数を実行する仕組みがあります。
+これをプロパティと呼びます。
+
+::
+
+    >>> class Foo(object):
+    ...     _bar = 1
+    ...     @property
+    ...     def bar(self):
+    ...         return self._bar * 2
+    ...     @bar.setter   # これを定義しないと bar は読み込み専用になる
+    ...     def bar(self, value):
+    ...         return self._bar = value
+    ...
+    >>> foo = Foo()
+    >>> foo.bar
+    2
+    >>> foo.bar = 2
+    >>> foo.bar
+    4
+
+この ``@`` は、関数の章で紹介したデコレータの構文で、 property はデコレータとして使える関数です。
+
+属性へのアクセスの動作をフックする魔法で、普通の関数だと第一引数 (self) にインスタンスを束縛していましたが、
+property は getter や setter を呼び出す魔法がかかったオブジェクトを返すのです。
+
+継承
+-----
+
+``class Foo(BaseClass)`` のように定義すると継承することができます。
+
+インスタンスの属性にアクセスしようとした時、そのインスタンスに属性がなかったらクラスの属性を探していましたが、
+それでも見つからないと親クラスの属性を探します。
+
+この仕組で、メソッドのオーバーライドも、それ以外のクラス属性のオーバーライドも可能になります。
+
+    >>> class Foo(object):
+    ...     def foo(self):
+    ...         return 'foo'
+    ...     def bar(self):
+    ...         return 'bar'
+    ...
+    >>> class Bar(Foo):
+    ...     def foo(self):
+    ...         return 'Bar.foo' + super(Bar, self).foo()
+    ...
+    >>> bar = Bar()
+    >>> bar.foo()
+    'Bar.foo foo'
+    >>> bar.bar()
+    'bar'
+    >>> Foo.foo(bar)
+    'foo'
+
+親クラスの呼び出し ``super(Bar, self).foo()`` が面倒なので、多重継承をしていないのであれば
+直接親クラスを指定して ``Foo.foo(self)`` と書くこともできます。
+
+.. note::
+    Python 3 では ``super().foo()`` と書けるようになります。
+
+classmethod, staticmethod
+--------------------------
+
+php で言う static メソッドは ``staticmethod`` というデコレータで実現します。
+これは関数にかかっている、属性アクセスされた時に第一引数にインスタンスを束縛する、という魔法を解除します。
+
+::
+
+    >>> class Foo(object):
+    ...    @staticmethod
+    ...    def bar():  # self が無いよ!
+    ...        return 1
+    ...
+    >>> foo = Foo()
+    >>> foo.bar()
+    1
+
+また、インスタンスではなくてクラスを第一引数に束縛する魔法をかける ``classmethod`` というデコレータもあります。
+classmethod の魔法は、インスタンス経由でもクラス経由でも使えるようになっています。
+php で言う ``new static()`` を実現するために、ファクトリー関数は staticmethod ではなく classmethod を使ったほうが
+良いでしょう。
+
+::
+
+    >>> class Foo(object):
+    ...     @classmethod
+    ...     def bar(cls):
+    ...         return cls()
+    ...
+    >>> class Bar(Foo):
+    ...     pass
+    ...
+    >>> bar = Bar.bar()  # クラス属性として実行しても第一引数にクラスが束縛される.
+    >>> bar
+    <__main__.Bar object at ...>
+    >>> bar.bar()        # インスタンス属性として呼び出してもクラスが束縛される.
+    <__main__.Bar object at ...>
    string.rst
    container.rst
    function.rst
+   class.rst