Python3超基礎(6)

Pythonのクラス

基本的なクラス定義。コンストラクタ、デストラクタ、メソッドの第1引数にselfが必ず入る(別名でも可能だが、慣例的にself)selfは自身のインスタンスを表す。呼び出し側は引数で指定することはできない。

注意点は、下記の例だとcountの変数。ほかのモダン言語の場合はインスタンス変数だが、Pythonでは異なり、ここの位置で定義される変数はクラス変数である。

インスタンス変数の初期化は、__init__コンストラクタ内で初期化なりすること。

あと、オーバーロードメソッドの定義はできない。

クラスメソッドに関しては、@classmethodに続けてメソッド定義を行うとインスタンスなしで実行できる。引数にクラスを渡すので、クラスに依存する処理が行える。

# study8.py
# Python Class sample
class MyClass:
  count = 0 # クラス変数(スタティック)

  def __init__(self): # コンストラクタ
    print("MyClass::constructor")
    self.name = "" # インスタンス変数(メンバ変数)
    MyClass.count += 1 # クラス変数にカウントアップ
    print("count="+str(MyClass.count))

  def __del__(self): # デストラクタ
    print("MyClass::desctructor")
    MyClass.count -= 1 # クラス変数にカウントダウン
    print("count="+str(MyClass.count))

  def __str__(self): # インスタンスが文字列に変換された時の処理
    return "MyClass is " + self.name

  def getName(self): # メソッド
    print("MyClass::geName")
    return self.name

  def setName(self, name): # メソッド
    print("MyClass::seName")
    self.name = name
  
  @classmethod
  def function(cls): # クラスメソッド(これとは別にスタティックメソッドもある)
    print("MyClass class method:{}".format(cls))

別のスクリプトからインスタンス化するときはモジュールを通じて行う。

import study8 as test

ins1 = test.MyClass()
ins1.name = "hoge"
ins2 = test.MyClass()
ins2.setName("huga")

print(str(ins1))
del ins1 #明示的に削除
print(str(ins2))

test.MyClass.function()

まぁ想像通りの動作をする。

> python3 study8a.py

MyClass::constructor
count=1
MyClass::constructor
count=2
MyClass::seName
MyClass::geName
MyClass is hoge
MyClass::desctructor
del : hoge
count=1
MyClass::geName
MyClass is huga
MyClass class method:<class 'study8.MyClass'>

注意点 インスタンス変数の初期化について

C++やC#などのインスタンス変数に関して、コンストラクタで初期化しなくても、変数自体は存在しており、C++では未初期化状態、C#では自動で初期化される。Pythonのインスタンス変数は後からでも変数が追加できる性質上、インスタンス変数に対して、何もしないと結果的にまだ存在しない状態になるので注意する必要がある。なので必ずコンストラクタで初期化すること。

クラス継承

単一、多重継承が可能である。親クラスと同一名のメソッドを定義した場合は(コンストラクタもそうなる)、自身のインスタンスのメソッドが優先される。なので、親クラスと同一名称のメソッドを定義すると、オーバーライドになる。

上記のインスタンス変数の初期化についても同様で、必ずコンストラクタ内で、親クラスのコンストラクタを実行して、親クラスが持つメンバ変数も初期化しておくこと。

# Python Class sample
class MyClass:
  count = 0 # クラス変数(スタティック)

  def __init__(self): # コンストラクタ
    print("MyClass::constructor")
    self.name = "" # インスタンス変数(メンバ変数)
    MyClass.count += 1 # クラス変数にカウントアップ
    print("count="+str(MyClass.count))

  def __del__(self): # デストラクタ
    print("MyClass::desctructor")
    print("del : "+self.name)
    MyClass.count -= 1 # クラス変数にカウントダウン
    print("count="+str(MyClass.count))

  def __str__(self): # 暗黙的に文字列に変換された時の処理
    return "MyClass is " + self.getName()

  def getName(self): # メソッド
    print("MyClass::geName")
    return self.name

  def setName(self, name): # メソッド
    print("MyClass::seName")
    self.name = name

  @classmethod
  def function(cls): # クラスメソッド(これとは別にスタティックメソッドもある)
    print("MyClass class method:{}".format(cls))

class MyClassDerived(MyClass):
  def __init__(self, age):
    print("MyClassDerived::constructor")
    super().__init__()
    self.age = age
  
  def __del__(self):
    print("MyClassDerived::desctructor")
  
  def __str__(self):
    return "MyClassDerived is " + super().getName()
  
  def getAge(self):
    print("MyClassDerived::getAge")
    return self.age

import study8 as test

ins1 = test.MyClass()
ins1.name = "hoge"
ins2 = test.MyClass()
ins2.setName("huga")

print(str(ins1))
del ins1 #明示的に削除
print(str(ins2))

test.MyClass.function()

print("-----")

ins3 = test.MyClassDerived(24)
ins3.setName("hoge child")
ins4 = test.MyClassDerived(44)
ins4.setName("huga child")

print(str(ins3))
print(str(ins4))

実行結果

> python3 study8a.py

MyClass::constructor
count=1
MyClass::constructor
count=2
MyClass::seName
MyClass::geName
MyClass is hoge
MyClass::desctructor
del : hoge
count=1
MyClass::geName
MyClass is huga
MyClass class method:<class 'study8.MyClass'>
-----
MyClassDerived::constructor
MyClass::constructor
count=2
MyClass::seName
MyClassDerived::constructor
MyClass::constructor
count=3
MyClass::seName
MyClass::geName
MyClassDerived is hoge child
MyClass::geName
MyClassDerived is huga child
MyClass::desctructor
del : huga
count=2
MyClassDerived::desctructor
MyClassDerived::desctructor

Pythonのメンバのスコープについて

他のオブジェクト指向な言語と同様にスコープ機能を使って、パブリック・プライベートなメソッドやインスタンス変数を疑似的に定義できる。(なぜ疑似的なのかは省略、ちゃんとサポートしているわけではないらしい)

やり方は、メソッドや変数名にプレフィックスとして、__(アンダースコア2つ)を付ける

# study9.py
# Python Class sample
class MyClass:
  count = 0 # クラス変数(スタティック)

  def __init__(self): # コンストラクタ
    self.name = ""
    self.age = 0

  def setName(self,name):
    self.name = name
  
  def __setAge(self,age):
    self.age = age

ins1 = MyClass()
ins1.setName("taro")
ins1.__setAge(12)

実行結果。__setAge()をコールしているところでエラーになる。

> python3 study9.py
Traceback (most recent call last):
  File "study9.py", line 17, in <module>
    ins1.__setAge(12)
AttributeError: 'MyClass' object has no attribute '__setAge'

正直、言語的にサポートしてないので、無理に使う必要はない気がする。上記の方法も完璧ではないので、「Pythonは基本的に全部publicである」と思っておいたほうがいいような気がする。

超基礎(1)~(6)まで一気に備忘録を残した。取りこぼしはありそうだが、まぁとりあえず使えるようにはなったかと。Python Lintとかのコーディングルールとかもあるけど保留。

あとは、便利なモジュールの使い方を応用編としてちょっとずつ学んでいく。pandas, numpy, matplot, threadingとか必要に迫られたらやっていくでしょう。

-->