ひさふぃの日記

DjangoとPythonとLaravelが好き。大阪でフリーランスエンジニアやってます。

入門Python3のメモ 6章|オブジェクトとクラス

Pythonの入門書として各所でオススメされている入門Python3。

shikouno.hatenablog.com

前半7章までで基本をおさらいし、後半8章からは他分野で活用できそうなちょっとした応用編がオールインワン。みんなのPython 第4版の次に読むとちょうどいいレベル感。

その際、初めて知る箇所や曖昧でいつも検索する箇所を後で見返せるようにメモしておこうかと。

そのため、このメモは僕の知識的偏りから生み出されたものであることを最初にお断りしておきます。気になった方は書籍を購入してください。

入門 Python 3

ちなみに、1章は導入なのでメモがありませんが、1.2 Pythonと多言語の比較は面白いので必見!著者の冗談も随所に入っていて、面白くていい本です。

英語版はPDFが無料で公開されています。よろしければ!

Introducing Python

クラス継承時の用語

  • 6.3 継承

継承元のクラス:親、スーパークラス、基底クラス

継承先のクラス:子、サブクラス、派生クラス

オブジェクト指向の専門用語では、子 is a 親をis-a関係と言う。

親メソッドに追加

  • 6.6 superによる親への支援要請

親クラスのメソッドを使いたい場合はsuper()を使う。

コード例では親クラスのメソッドが簡単なため全ての処理をオーバーライドして書いた方が早いですが、コンストラクタの変数が多い場合や複雑なメソッドの場合には真価を発揮しそうですね。

また、親クラスに変更があった場合に反映される点もメリット。

>>> class Parent:
...     def caluc(self, num):
...         num += num
...         return num
...
>>> class Child(Parent):
...     def caluc(self, num):
...         num = super().caluc(num)
...         num *= num
...         return num
...
>>> hoge = Child()
>>> hoge.caluc(3)
36

ゲッターとセッター

  • 6.8 プロパティによる属性値の取得、設定
  • 6.9 非公開属性のための名前のマングリング

Pythonではゲッターとセッターは必要がないのですが、直接アクセスがどうしても嫌な場合はプロパティを用いるようです。他の言語を知らないのでピンときませんが。

クラス外から変数へアクセスできないようにするためには、変数名の前にふたつのアンダースコアをつけます。これで、セッターやゲッターを用いたアクセスのみに制限することができます。

しかし、無理やりアクセスすることは可能。お行儀の良いPythonユーザーはそんなことしませんよね。

ゲッターとセッターを定義する際はproperty()メソッドかデコレータを用いる方法がありますが、入門Python3を読んでいていまひとつ理解が足りなかったので公式ドキュメントを読んで補いました。

2. 組み込み関数 — Python 3.6.1 ドキュメント

property()が代入されたクラス内変数を参照すると第一引数に渡したゲッターメソッドが呼び出されます。

>>> class Prop:
...     def __init__(self):
...         self.__arg = None
...     def get_arg(self):
...         print("do getter")
...         return self.__arg
...     def set_arg(self, input_arg):
...         print("do setter")
...         self.__arg = input_arg
...     arg = property(get_arg, set_arg)
...
>>> x = Prop()
>>> x.set_arg("hoge")
do setter
>>> x.get_arg()
do getter
'hoge'
>>> x.arg
do getter
'hoge'

メソッドの前に定義がわかるデコレータの方が僕は好みですね。デコレータの場合、メソッドは変数のように扱います。

>>> class Deco:
...     def __init__(self):
...         self.__arg = None
...     @property
...     def arg(self):
...         print("do getter")
...         return self.__arg
...     @arg.setter
...     def arg(self, input_name):
...         print("do setter")
...         self.__arg = input_name
...
>>> x = Deco()
>>> x.arg = "hoge"
do setter
>>> x.arg
do getter
'hoge'

クラスメソッドと静的メソッド

  • 6.10 メソッドのタイプ

通常のメソッドはインスタンスに紐づいていますが、クラスメソッドはクラス自体に紐づきます。そのため、クラスのインスタンスが何回作られたかを調べるメソッドを定義できるのです。

直前に@classmethodを書いたメソッドがクラスメソッドで、クラス範囲内・メソッド範囲外に書かれた変数がクラス変数になります。また、クラスメソッドの第1引数はclsを使うようです。

>>> class ClassMethod:
...     count = 0
...     def __init__(self):
...         ClassMethod.count += 1
...     @classmethod
...     def counter(cls):
...         return cls.count
...
>>> ClassMethod.counter()
0
>>> hoge = ClassMethod()
>>> fuga = ClassMethod()
>>> ClassMethod.counter()
2

静的メソッドはインスタンス化せずに実行することができますが、活用場面が思い浮かばない。

直前に@staticmethodを書いたメソッドが静的メソッドで、selfやclsのような第1引数を取りません。

>>> class StaticMethod:
...     @staticmethod
...     def static():
...         print("do static method")
...
>>> StaticMethod.static()
do static method

インスタンス化して渡す

2つ以上のクラスの処理を取り入れたい場合、コンポジション・集約の方が良いことがある。コンポジションって言うんですね。

オブジェクト指向的に言うとhas-a関係。インスタンス保有するクラス has a インスタンス化されるクラス

コードを書くときに意識せずに使っていますね。各処理や変数をもつクラスを、インスタンス化してMainクラスに渡す。

>>> class Hoge:
...     def __init__(self, arg):
...         self.arg = arg
...     def do(self):
...         print("Hoge has " + self.arg)
...
>>> class Fuga:
...     def __init__(self, arg):
...         self.arg = arg
...     def do(self):
...         print("Fuga has " + self.arg)
...
>>> class Main:
...     def __init__(self, arg):
...         self.hoge = Hoge(arg)
...         self.fuga = Fuga(arg)
...     def do(self):
...         self.hoge.do()
...         self.fuga.do()
...
>>> a = Main('test arg')
>>> a.do()
Hoge has test arg
Fuga has test arg

モジュールとクラスの使い分け

  • 6.14 モジュールではなくクラスとオブジェクトを使うべきなのはいつか

複数のインスタンスが必要なとき、継承が必要なとき、変数が複数ありいくつかの関数に引数として渡すとき。

繰り返しがある場合はクラスを使う方が良さそうですね。本格的なオブジェクト指向を勉強するまで、頭にとどめておきたい内容。

さいごに

クラスは曖昧にしていたところが多かったので3章に次ぐボリュームになってしまいました。

特にデコレータと継承・コンポジションは、腰を据えて取り組んで腑に落とすことができたので良かったです。

最後までお読みいただきありがとうございます。それじゃ!

入門 Python 3

入門 Python 3