初めてのPython(24章前編)

class ステートメント

def ステートメントと同じような動き

  • 該当モジュールが読み込まれた時に実行される
  • クラス名に指定したクラスオブジェクトを生成し、クラス名の変数に代入する

クラスは名前空間として考えられる

  • クラスのなかで代入された変数はクラスのローカルスコープ
  • クラスメソッドの中で代入された変数はメソッドのローカルスコープ
  • クラスの属性はインスタンスにすべて引き継がれる*1
[kobakoba0723@fedora13-intel64 ~]$ cat sample.py
#!/usr/bin/env python

class C1(object):
  data = "spam"
  
  def setdata(self, value):
    self.data = value
  
  def printdata(self):
    print "data: ", self.data


if __name__ == '__main__':
  x = C1()
  y = C1()
  
  print '(x.data, y.data, C1.data) = (%s, %s, %s)' % (x.data, y.data, C1.data)
  
  C1.data = "egg"
  print '(x.data, y.data, C1.data) = (%s, %s, %s)' % (x.data, y.data, C1.data)
  
  x.setdata("onion")
  print '(x.data, y.data, C1.data) = (%s, %s, %s)' % (x.data, y.data, C1.data)

[kobakoba0723@fedora13-intel64 ~]$ python sample.py
(x.data, y.data, C1.data) = (spam, spam, spam)
(x.data, y.data, C1.data) = (egg, egg, egg)
(x.data, y.data, C1.data) = (onion, egg, egg)
[kobakoba0723@fedora13-intel64 ~]$ 

クラスメソッド

基本的には通常の関数オブジェクトと同じ
大きな違いは、『呼び出される時に第1引数にインスタンスオブジェクトを必ず受け取る』こと

そのため、メソッドの呼び出しに使うインスタンス.メソッド(引数...)は、自動的に以下に読み換えられる。

クラス.メソッド(インスタンス, 引数...)

[kobakoba0723@fedora13-intel64 ~]$ python
Python 2.6.4 (r264:75706, Apr  1 2010, 02:55:51) 
[GCC 4.4.3 20100226 (Red Hat 4.4.3-8)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sample
>>> x = sample.C1()
>>> x.setdata("onion")
>>> x.printdata()
data:  onion
>>> sample.C1.setdata(x, "carrot")
>>> sample.C1.printdata(x)
data:  carrot
>>> 
スーパークラスの__init__呼び出し

クラス.メソッド(インスタンス, 引数...)の構文を使うことで、スーパークラスの初期化処理をサブクラスから呼び出すことが出来る。
サブクラスの中で、スーパークラスの属性を利用する必要があって、初期化処理をしたい時に使う

[kobakoba0723@fedora13-intel64 ~]$ cat sample.py 
#!/usr/bin/env python

class C1(object):
  def __init__(self, value):
    self.x = value
  
class C2(C1):
  def __init__(self, value_x, value_y):
    self.y = value_y
    C1.__init__(self, value_x)
  
  def printdata(self):
    print "(x, y) = (%d, %d)" % (self.x, self.y)

if __name__ == '__main__':
  a = C2(1, 2)
  a.printdata()

[kobakoba0723@fedora13-intel64 ~]$ python sample.py 
(x, y) = (1, 2)
[kobakoba0723@fedora13-intel64 ~]$ 

抽象クラス

クラスの機能の一部をサブクラスに依存するクラスのこと
以下の場合のC1が抽象クラス

[kobakoba0723@fedora13-intel64 ~]$ cat abstract.py 
#!/usr/bin/env python

class C1(object):
  def method(self):
    print 'in C1.method'
    self.abstract()
  
  def abstract(self):
    assert 0, 'abstract must be defined'

class C2(C1):
  def abstract(self):
    print 'in C2.abstract'

class C3(C1):
  pass

if __name__ == '__main__':
  x = C2()
  x.method()
  
  y = C3()
  y.method()

[kobakoba0723@fedora13-intel64 ~]$ python abstract.py
in C1.method
in C2.abstract
in C1.method
Traceback (most recent call last):
  File "abstract.py", line 23, in <module>
    y.method()
  File "abstract.py", line 6, in method
    self.abstract()
  File "abstract.py", line 9, in abstract
    assert 0, 'abstract must be defined'
AssertionError: abstract must be defined
[kobakoba0723@fedora13-intel64 ~]$ 

C1のabstractメソッドにassertステートメントを使うことで、サブクラスでabstractメソッドを定義しないとエラーを起こすことが出来る。
raiseステートメントでも同様のことは可能で、C1.abstractメソッドは以下のようになる。

  def abstract(self):
    raise NameError, 'abstract must be defined'

*1:インスタンスオブジェクト側でクラス属性と同名の属性を代入した場合、クラス属性が変更されるのではなく、インスタンス属性として新しくて意義される