プロジェクト

全般

プロフィール

4章学習記録 » 履歴 » バージョン 1

Tatsuya ISHIGAKI, 2025/06/23 12:52

1 1 Tatsuya ISHIGAKI
# 第4章 Python のクラス
2
**Python 実践レシピ** (技術評論社)
3
4
## 4.1 class 構文
5
- class 定義
6
```python
7
class クラス名:
8
    属性 = 値
9
10
    def メソッド名(self, ...):
11
        メソッド処理
12
```
13
14
例:Userクラス (class_sample.py)
15
```python
16
class User:
17
    type = None
18
19
    def __init__(self, name, age, address):
20
        self.name = name
21
        self.age = age
22
        self.address = address
23
24
    def increment_age(self):
25
        self.age += 1
26
    
27
    def start_name(self):
28
        if len(self.name) > 0:
29
            return self.name[0]
30
        else:
31
            return ""
32
```
33
34
- インスタンス化
35
```python
36
from class_sample import User
37
38
user1 = User("smith", 41, "Shizuoka")
39
user2 = User("tatsuya", 20, "Nagoya")
40
```
41
42
- (インスタンス)メソッド定義で第1仮引数とする `self` は、インスタンスが渡される
43
  - メソッドの実引数 1, 2, ... 番目は、仮引数 2, 3, ... 番目に対応する
44
  - メソッド定義時の `self` は別の名前でも良いが、慣例としてこの名前
45
46
## 4.2 属性とメソッド
47
- `__init__()` コンストラクターメソッド
48
- データ属性
49
  - クラス変数 (全インスタンスで共有される) `User.type` `User.type = "SE"`
50
  - インスタンス変数 (クラス定義内で `self.変数名` で使用) `user2.name` `user2.age = 30`
51
  - **注意:追加調査と実験で確かめた**
52
    - 同名のクラス変数とインスタンス変数が存在すると、インスタンス変数参照が優先される
53
      - クラス変数へのアクセスメソッドなどで対応できる
54
    - クラス変数への代入のつもりで、インスタンスを使用して代入すると、新たに同名のインスタンス変数が生成されてしまう (その後はインスタンスオブジェクトからは直接クラス変数参照はできない)
55
      ```python
56
      >>> from class_sample import User
57
      >>>
58
      >>> user = User("smith", 41, "Shizuoka")
59
      >>> User.type = "SE"  # クラス変数へ代入
60
      >>> User.type  # クラス変数が参照されている
61
      'SE'
62
      >>> user.type  # クラス変数が参照されている
63
      'SE'
64
      >>> user.type = "NEET"  # ここで user のインスタンス変数 type が新規定義されて値代入される
65
      >>> User.type  # クラスからはクラス変数が参照されている
66
      'SE'
67
      >>> user.type  # 新規定義されたインスタンス変数を参照してしまう
68
      'NEET'
69
      >>>
70
    ```
71
72
- メソッド
73
  - インスタンスメソッド
74
    - 暗黙的に第1引数へ **インスタンスオブジェクト** を渡して実行される
75
  - クラスメソッド
76
    - `@classmethod` デコレーターをメソッドに使って定義する
77
    - 暗黙的に第1引数へ **クラスオブジェクト** を渡して実行される
78
  - 静的メソッド
79
    - `@staticmethod` デコレーターをメソッドに使って定義する
80
    - 暗黙的に渡されるオブジェクトは無い
81
- 特殊メソッド
82
  - 組み込み関数、演算子(`==`, `+` 等)利用時の動作等の定義
83
  - 以下、主な特殊メソッド
84
85
|特殊メソッド|概要|
86
|:--|:--|
87
|`__init__()`|コンストラクターメソッド|
88
|`__repr__()`|オブジェクトを `print()` 関数で出力した時の文字列表現を定義|
89
|`__len__()`|オブジェクトを `len()` 関数へ渡した際の値を定義|
90
|`__call__()`|呼び出し可能化|
91
|`__str__()`|文字列型への変換|
92
|`__eq__()`|演算子 `==`|
93
|`__lt__()`|演算子 `<`|
94
|`__le__()`|演算子 `<=`|
95
|`__ne__()`|演算子 `!=`|
96
|`__gt__()`|演算子 `>`|
97
|`__ge__()`|演算子 `>=`|
98
|`__add__()`|演算子 `+`|
99
|`__sub__()`|演算子 `-`|
100
|`__mul__()`|演算子 `*`|
101
|`__truediv__()`|演算子 `/`|
102
103
- インスタンスメソッドのプロパティ化
104
  - `@property` デコレーターを使用すると、括弧無しで計算(処理)結果を取得できる
105
    - これは **setter** の設定となるが、**getter**, **deleter** も property で適用できる
106
    - [property リファレンス](https://docs.python.org/ja/3.13/library/functions.html#property)
107
- クラスメソッド使用例
108
  - コンストラクタへの引数に依らないインスタンスを生成する
109
110
## 4.3 継承
111
- 継承構文
112
  - クラス定義の際に、クラス名に続けて括弧内に親クラス名を記述
113
  ```python
114
  class 子クラス名(親クラス名):
115
      pass
116
  ```
117
- `unittest.TestCase` を継承する例
118
```python
119
import unittest
120
121
class TestSample(unittest.TestCase):
122
    def setUp(self):
123
        self.target = "foo"
124
125
    def test_upper(self):
126
        self.assertEqual(self.target.upper(), "FOO")
127
```
128
- 多重継承
129
  - Python では多重継承サポート
130
  - 以下の様に記載し、この場合は Child インスタンスの属性・メソッド参照では、Child → Parent1 → Parent2 の順に定義を探して、最初に見つかったものが対象となる
131
  - 多重継承の継承順位を **MRO (Method Resolution Order)** という仕組みで検索する
132
  ```python
133
  class Child(Parent1, Parent2):
134
      pass
135
  ```
136
- フレームワークでは継承使用を前提としたクラスが多く使われる
137
138
## 4.4 dataclass
139
- `dataclasses.dataclass` はクラスデコレーター
140
  - データ組み合わせとしての型を簡単に定義
141
  - `__init__()` `__repr__()` を定義しなくてもよい
142
  - 引数 `frozen=True` により、イミュータブルとなり値変更できない型として定義できる
143
  - デフォルト値を指定した属性を指定すると、インスタンス化でのコンストラクタ実引数が可変化できる
144
  ```python
145
  # dataclass.py
146
  from dataclasses import dataclass
147
  
148
  @dataclass
149
  class User:
150
      name: str
151
      age: int
152
      address: str
153
  
154
  @dataclass(frozen=True)
155
  class FrozenUser:
156
      name: str
157
      age: int
158
      address: str
159
160
  @dataclass
161
  class User:
162
      name: str
163
      age: int
164
      address: str
165
      active: bool = False  # コンストラクタ引数は3 or 4個
166
  ```
167
  - `dataclasses` モジュールの `asdict()` `astuple()` によって、dataclass のインスタンスを変換できる
168
    - `asdict()` 属性名の文字列がキーとなる辞書
169
    - `astuple()` 属性値のみのタプル
170
- dataclass の属性値の型ヒントは「ヒント」なので、使用時には自由な型を指定できてしまう事に注意
171
172
173
## 4.5 オブジェクト関連関数
174
|関数名|解説|返り値|
175
|:--|:--|:-:|
176
|id(object)|識別値|int|
177
|type(object)|型オブジェクト取得|type|
178
|isinstance(object, classinfo)|object が classinfo (または classinfo の継承先クラス) のインスタンスであるか判定|bool|
179
|issubclass(class, classinfo)|class が classinfo のインスタンスであるか判定|bool|
180
|help(object)|object のヘルプを表示|None|
181
|dir(object)|object が持つ属性・メソッドのリストを返す|list|
182
183
- `isinstance()`
184
  - 第2引数に型のタプルを渡す使用法がある
185
    - タプルのどれかのインスタンスであれば True
186
    ```python
187
    >>> isinstance(True, bool)
188
    True
189
    >>> isinstance(True, int)  # bool は int を継承している
190
    True
191
    >>> isinstance(True, str)
192
    False
193
    >>> isinstance(True, (int, str))
194
    True
195
    >>>
196
    ```
197
  - フレームワークの作成時によく使用される
198
    - フレームワークに含まれる class の継承を想定する場合など
199
- `dir()`
200
  - すべての属性・メソッドが取得されるので、デバッグ時等に使用
201
  ```python
202
  >>> dir("test")
203
  ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'removeprefix', 'removesuffix', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
204
  ```
205
- 上記のオブジェクト関連関数は組み込み関数なので、インポート不要
206
  - これらの関数と同名の変数等を使用すると混乱を生じるので避ける
207
    - (例) `def func(type):` ※仮引数に **type** を使用