プログラムが複雑になってきて、周りの人からデザインパターンを使ってすっきりさせた方が良いとのアドバイスを頂いたのですが。。。
それ以前に、クラスについて(1)継承をうまく使いこなせない(2)クラス引数のselfがどうも理解できない、の2つの理由でちょっと避けていました。腹をくくって、デザインパターンを使うには、まずクラスをきちんと理解して使うことを考えて情報収集した覚えです。
参考になった記事です
Pythonによるデザインパターン5原則
この中で、「継承より集約を使う方が良い。」
継承より合成ってなに?
この記事でも、「なんで継承より合成なのか?名前空間をごっちゃにするから」との指摘がありました。
継承にこだわるのではなくもっと自由に考えればよいというので少しバリアが下がりました。
もう一つの苦手な引数のselfについてですが
Mastering Python クラス変数とインスタンス変数ってなに?
が大変参考になりました。
記事の中での問いとして「何でインスタンス変数には頭に self が必要なの?」
その答え「 関数の中で使われた "値" は、 "オブジェクトの属性" に代入してもらわないと外から見えないから。」
例えば、こんなTestクラスを作ります。関数(クラスの中ではメソッドとよぶ)の中にselfをつけた変数(属性)とつけない変数を作っています。
クラス定義の直下に置かれる変数はクラス変数と呼ばれ、すべてのインスタンスに同じ値が入ります。
class Test():
val1=1 #クラス変数
val2=2 #クラス変数
def method1(self):
print(self.val1)
val3=3
print(val3)
def method2(self):
self.val4 = 4
print(self.val4)
print(self.val1)
print(val3)
print(val2)
def method3(self):
print(self.val4)
print(self.val1)
print(val2)
インスタンスを作って各メソッドを実行してみます。
test=Test()
test.method1()
>>>1
>>>3
selfをつけた変数とつけない変数ともにprintされます。
test.method2()
>>>4
>>>1
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-13-474bb1b328ca> in <module>
----> 1 test.method2()
<ipython-input-10-2dc889eb6ddc> in method2(self)
12 print(self.val4)
13 print(self.val1)
---> 14 print(val3)
15 print(val2)
16
NameError: name 'val3' is not defined
メソッド2を呼び出すと、メソッド1の中で代入されたval3については見ることができません。クラス変数についてもselfをつけると呼び出すことができます。
test.method3()
>>>4
>>>1
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-14-74791bccecc6> in <module>
----> 1 test.method3()
<ipython-input-10-2dc889eb6ddc> in method3(self)
18 print(self.val4)
19 print(self.val1)
---> 20 print(val2)
NameError: name 'val2' is not defined
クラス変数のval2は、selfをつけないと呼び出せません。
test.val2
>>>2
val2(クラス変数)は属性として呼び出すことができます。
test.val3
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-19-3ef0c53489c6> in <module>
----> 1 test.val3
AttributeError: 'Test' object has no attribute 'val3'
selfをつけなかったval3については呼び出せません。
関数内の変数は関数内だけで有効になります。クラスの場合は、クラス内に関数(この場合は、メソッドと呼ぶ)を配置したとき、各メソッド間で変数を共有するためには、self.をつけた変数ならば共有することができます(属性)。なので、初期化メソッド(init)で定義する変数にselfをつけるのはそのためで、ここで定義した変数は、他のメソッドでもself.で呼び出して使うことができます。
名前空間ってなに? 2.3. インスタンス変数を参照するとき
class User:
def __init__(self, name, age):
self.name = name
age = age # <- self を明示しない。
user = User('山田太郎', 20)
user.name
user.age # AttributeError
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-44-878d7e012ca1> in <module>()
6 user = User('山田太郎', 20)
7 user.name
----> 8 user.age # AttributeError
AttributeError: 'User' object has no attribute 'age'
上記の著者の別の記事で
スコープってなに?
名前空間を適切に分けること
オブジェクト指向ってなに?
などの記事も大変参考になります。
この中の記事で「オブジェクト指向とは何かについて考えるために、
まず「どんな時に関数で書いて、どんな時にメソッドで書くべきなんだろう?」」との問いがあります。
著者の回答として以下の3点が上げられています。
Step 1. クラスに限定されているかでクラスを設計する。
Step 2. 現実世界のモデリングとしてクラス設計をする。
Step 3. コードの整理術としてクラス設計をする。