プロジェクト

全般

プロフィール

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

Tatsuya ISHIGAKI, 2025/07/09 12:44

1 1 Tatsuya ISHIGAKI
# 第9章 データ型とアルゴリズム
2
3
## 9.1 ソート sorted, sort, operator
4
5
|関数、メソッド|解説|戻り値|
6
|---|---|---|
7
|`sorted(iterable, *, key=None, reverse=False)`|イテラブルを引数で受け取り、ソートした結果を返す|list|
8
|`reversed(seq)`|引数のシーケンスを逆順にした結果を返す|**イテレーター** (reverse iterator)|
9
|`list.sort(*, key=Noen, reverse=False)`|リストをソートした結果に変更する|None|
10
|`list.reverse()`|リストを逆順にした結果に変更する|None|
11
12
- sorted() 関数
13
  - `iterable` ソート対象のイテラブルオブジェクト
14
  - `key` ソートキーを取得する呼び出し可能オブジェクト
15
  - `reverse` True:降順ソート、False:昇順ソート
16
  - **戻り値** リスト
17
18
  ```python
19
  >>> lt1
20
  [3, 4, 1, 3, 0, 5, 1, 3, 0, 0]
21
  >>> sorted(lt1)
22
  [0, 0, 0, 1, 1, 3, 3, 3, 4, 5]
23
  >>> lt2 = [3, 4, 2.2, -4, 1]
24
  >>> lt2
25
  [3, 4, 2.2, -4, 1]
26
  >>> sorted(lt2)
27
  [-4, 1, 2.2, 3, 4]
28
  >>> sorted(["yen", "dollar", "euro"])
29
  ['dollar', 'euro', 'yen']
30
  >>> lt3 = [2, 0, "ABC"]  # 文字列と数値は比較できないのでソートはエラー
31
  >>> sorted(lt3)
32
  Traceback (most recent call last):
33
    File "<python-input-26>", line 1, in <module>
34
      sorted(lt3)
35
      ~~~~~~^^^^^
36
  TypeError: '<' not supported between instances of 'str' and 'int'
37
  ```
38
39
- リスト以外のイテラブルの例
40
  ```python
41
  >>> sorted((3, 5, 1.5, 4, 100, 2, 4))
42
  [1.5, 2, 3, 4, 4, 5, 100]                     # タプル
43
  >>> sorted({"c": 2, "a": 4, "b": 0})
44
  ['a', 'b', 'c']                               # 辞書はキーのリストに
45
  >>> sorted({"c": 2, "a": 4, "b": 0}.items())  
46
  [('a', 4), ('b', 0), ('c', 2)]                # 辞書をタプルイテラブルにすれば値も取得できる
47
  >>> sorted({5, 4, 1, 4, 9})
48
  [1, 4, 5, 9]                                  # 集合
49
  >>> sorted("SmithはPy3できますか")             # 文字列は一文字ずつ
50
  ['3', 'P', 'S', 'h', 'i', 'm', 't', 'y', 'か', 'き', 'す', 'で', 'は', 'ま']
51
  ```
52
53
- reversed() 関数
54
  - `seq` シーケンス型オブジェクト (※順番を持つコンテナー)
55
  - **戻り値** イテレーター (reverse iterator)
56
57
  ```python
58
  >>> seq
59
  [6, 1, 10, 3, 0]
60
  >>> rev_seq = reversed(seq)
61
  >>> rev_seq
62
  <list_reverseiterator object at 0x0000016A5E2E6EF0>  # イテレーター
63
  >>> list(rev_seq)  # リストに変換
64
  [0, 3, 10, 1, 6]   # 逆順になっている
65
  >>> list(rev_seq)  # イテレーターなので、全部取得後は空
66
  []
67
  >>> seq
68
  [6, 1, 10, 3, 0]   # 元のシーケンスはそのまま
69
  ```
70
71
- リストのメソッド sort(), reverse()
72
  - リストのメソッド sort(), reverse() は、**破壊的操作** であり、元のリストを変更する
73
74
  - (sort() のみ) `key`, `reverse` sorted() と同じ
75
  - **戻り値** None
76
77
  ```python
78
  >>> lt4
79
  [1, 0, 4, 2, 2, 3]
80
  >>> lt4.sort()  # ソート
81
  >>> lt4
82
  [0, 1, 2, 2, 3, 4]
83
  >>> lt5
84
  [8, 7, 4, 8, 2, 7]
85
  >>> lt5.reverse()  # 逆順
86
  >>> lt5
87
  [7, 2, 8, 4, 7, 8]
88
  >>> lt6
89
  [8, 0, 2, 4, 10, 2]
90
  >>> lt6.sort(reverse=True)  # 降順ソート
91
  >>> lt6
92
  [10, 8, 4, 2, 2, 0]
93
  ```
94
95
- key 引数
96
  - key を指定しない場合は、要素をそのまま `<` (小なり) で比較する
97
  - key に呼び出し可能オブジェクトを設定すると、ソート対象要素それぞれを変換した結果に対して `<` を適用してソートする
98
99
  ```python
100
  >>> seq_str = ["B", "D", "a", "c"]
101
  >>> sorted(seq_str)                 # そのままソート
102
  ['B', 'D', 'a', 'c']
103
  >>> sorted(seq_str, key=str.lower)  # 小文字にした結果でソート
104
  ['a', 'B', 'c', 'D']
105
  ```
106
107
- operator モジュール
108
  - key 引数に利用しやすい2つの関数 `itemgetter()` `attrgetter()` を提供
109
  - これらの関数は「呼び出し可能オブジェクト」を返すので、key 引数に渡せる
110
111
  **itemgetter()** ([_index_], [_key_] での取得に対応)
112
  ```python
113
  >>> from operator import itemgetter
114
  >>> data = [(1, 40, 200), (3, 10, 100), (2, 20, 300), (1, 30, 300)]  # 3要素のタプルが4組
115
  >>> sorted(data)  # keyを指定しないでソート
116
  [(1, 30, 300), (1, 40, 200), (2, 20, 300), (3, 10, 100)]  # インデックス0から順に比較し同じなら次のインデックスで比較
117
  >>> sorted(data, key=itemgetter(2))  # インデックス2でソートし、それ以外はもとの順番を保つ
118
  [(3, 10, 100), (1, 40, 200), (2, 20, 300), (1, 30, 300)]
119
  >>> sorted(data, key=itemgetter(2, 0))  # インデックス2でソートし同じ場合は0でソート
120
  [(3, 10, 100), (1, 40, 200), (1, 30, 300), (2, 20, 300)]
121
  ```
122
123
  ```python
124
  >>> from operator import itemgetter
125
  >>> dic = {'a': 2, 'c': 1, 'b': 3}
126
  >>> sorted(dic.items(), key=itemgetter(1))  # items()は2要素タプルとなるのでインデックス1を指定
127
  [('c', 1), ('a', 2), ('b', 3)]
128
  ```
129
130
  ```python
131
  >>> from operator import itemgetter
132
  >>> users = [{"name": "terada", "age": 35},
133
  ...          {"name": "suzuki", "age": 25},
134
  ...          {"name": "sugita", "age": 30}]
135
  >>> sorted(users, key=itemgetter("age"))  # 辞書のageキーを使ってソートする
136
  [{'name': 'suzuki', 'age': 25}, {'name': 'sugita', 'age': 30}, {'name': 'terada', 'age': 35}]
137
  ```
138
139
  **attrgetter()** (「.」 (ドット) での取得に対応)
140
  ```python
141
  >>> from operator import attrgetter
142
  >>> from datetime import date
143
  >>> date(1970, 11, 28).month  # month属性で月を取得できる
144
  11
145
  >>> date(1970, 11, 28).day  # day属性で日を取得できる
146
  28
147
  >>> dates = [date(1989, 1, 4),
148
  ...          date(1970, 11, 28),
149
  ...          date(1984, 3, 4)]  # ソート対象のdateオブジェクトのリストを定義
150
  >>> sorted(dates, key=attrgetter("month", "day"))  # "month"でソートし、次に"day"でソート
151
  [datetime.date(1989, 1, 4), datetime.date(1984, 3, 4), datetime.date(1970, 11, 28)]
152
  ```
153
154
  ## 9.2 さまざまなコンテナー型を扱う collections
155
156
  - データの件数をカウントする Counter
157
    - 各要素が何回現れたが数えて保持する
158
      - 辞書の拡張であり、要素をキー、回数を値として保持する
159
    - `class Counter([iterable-or-mapping])`
160
      - `iterable-or-mapping` Counter オブジェクトの初期値を指定するマッピングオブジェクトまたはイテラブルオブジェクト
161
162
    ```python
163
    >>> from collections import Counter
164
    >>> cnt = Counter("AAABBBCCCABCAAC")
165
    >>> cnt
166
    Counter({'A': 6, 'C': 5, 'B': 4})
167
    >>> cnt["A"]  # 辞書と同様の指定で、回数を取得
168
    6
169
    >>> cnt["D"]  # 通常の辞書と異なり、存在しないキー指定でも例外にならず 0 を返す
170
    0
171
    >>> "A" in cnt  # 存在チェックは可能
172
    True
173
    >>> "D" in cnt
174
    False
175
    >>> cnt["E"] = 0  # 直接代入
176
    >>> cnt
177
    Counter({'A': 6, 'C': 5, 'B': 4, 'E': 0})  # 0 も計数される
178
    >>> cnt["E"]
179
    0
180
    >>> "E" in cnt  # 回数 0 で存在する場合
181
    True
182
    ```
183
184
    |メソッド名|解説|戻り値|
185
    |---|---|---|
186
    |`elements()`|キーを、値の数だけ繰り返すイテレーターを返す|イテレーター|
187
    |`most_common([n])`|値が大きい順に、キーと値のペアを返す<br>`n` 指定の場合、最大 n 件の要素を返す|list|
188
    |`subtract(iterable-or-mapping)`|要素からイテラブルまたはマッピングオブジェクトの値を減算する|None|
189
    |`update(iterable-or-mapping)`|(同上) 加算する|None|
190
191
    - メソッド使用例
192
193
      ```python
194
      >>> cnt = Counter("AAABBBCCCABCAAC")
195
      >>> cnt
196
      Counter({'A': 6, 'C': 5, 'B': 4})
197
      >>> list(cnt.elements())
198
      ['A', 'A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', 'C', 'C', 'C']
199
      >>> cnt.most_common(2)
200
      [('A', 6), ('C', 5)]
201
      >>> cnt.subtract("CCCAA")
202
      >>> cnt
203
      Counter({'A': 4, 'B': 4, 'C': 2})
204
      >>> cnt.update("BBBDDDDD")
205
      >>> cnt
206
      Counter({'B': 7, 'D': 5, 'A': 4, 'C': 2})
207
      ```
208
209
    - 演算子 `+` `-` 各要素の値が計算される (値が 0 以下の要素は結果に含まれない)
210
    - 演算子 `&` `|` 各要素の値の最小、最大値 (片方にキーが無い場合は 0 と考える) (値が 0 以下の要素は結果に含まれない)
211
212
      ```python
213
      >>> cnt1
214
      Counter({'A': 6, 'C': 5, 'B': 4})
215
      >>> cnt2
216
      Counter({'B': 4, 'A': 2})
217
      >>> cnt1 + cnt2
218
      Counter({'A': 8, 'B': 8, 'C': 5})
219
      >>> cnt1 - cnt2
220
      Counter({'C': 5, 'A': 4})
221
      ```
222
223
- デフォルト値を持った辞書 defaultdict
224
  - 通常の辞書は存在しないキーを参照すると KeyError 例外が発生するが、defaultdict は例外発生ではなくデフォルト値を返す
225
  - `class defaultdict([default_factory[, ...]])`
226
    - `default_factory` 存在しないキーが参照されたときの値を返す、呼び出し可能オブジェクトを指定 (省略時は None で、通常の辞書と同じく例外送出)
227
    - また、`default_factory` には `int` `list` `dict` `set` が指定でき、それぞれ 0 または空のオブジェクトが返る defaultdict になる
228
 
229
    ```python
230
    >>> d = {'spam':100}  # 通常の辞書を作成
231
    >>> d['ham']  # 存在しないないキーを指定するとKeyErrorが発生する
232
    Traceback (most recent call last):
233
      File "<input>", line 1, in <module>
234
    KeyError: 'ham'
235
    >>> from collections import defaultdict
236
    >>> def value():  # デフォルト値を返す関数を定義
237
    ...     return 'default-value'
238
    ...
239
    >>> dd = defaultdict(value, spam=100)  # デフォルトにvalue()関数を指定
240
    >>> dd  # 生成されたオブジェクトを確認
241
    defaultdict(<function value at 0x10748cee0>, {'spam': 100})
242
    >>> dd['ham']  # 存在しないキーを指定するとデフォルト値が返る
243
    'default-value'
244
    >>> dd['spam']
245
    100
246
    ```
247
248
  - `default_factory` に `int` `list` を使用する例
249
250
    ```python
251
    >>> from collections import defaultdict
252
    >>> dd_int = defaultdict(int)   # デフォルト値は0
253
    >>> dd_int['spam']  # デフォルト値を確認
254
    0
255
    >>> dd_int['spam'] += 1  # 累算代入も使える
256
    >>> dd_int['spam']
257
    1
258
    >>> dd_list = defaultdict(list)  # デフォルト値は空のリスト
259
    >>> dd_list['spam']  # デフォルト値を確認
260
    []
261
    >>> dd_list['spam'].append('ham')
262
    >>> dd_list['spam'].append('egg')
263
    >>> dd_list['spam']
264
    ['ham', 'egg']
265
    ```
266
267
- データの挿入順序を維持する辞書 OrderedDict
268
  - Python3.7 より前の辞書は、データの挿入順序が維持されなかった為、挿入順序維持の辞書として OrderedDict が利用された
269
  - Python3.7 から組み込みの辞書がでーたの挿入順を維持するようになっているので、新たな開発では使用しない
270
271
  |メソッド名|解説|戻り値|
272
  |---|---|---|
273
  |`move_to_end(key, last=True)`|指定したキーを末尾 (last=True) または先頭 (last=False) へ移動する|None|
274
  |`popitem(last=True)`|末尾の要素 (last=True) または先頭の要素 (last=False) を取り出す|取り出したオブジェクト|
275
276
  ```python
277
  >>> from collections import OrderedDict
278
  >>> d = OrderedDict(one=1, two=2, three=3)
279
  >>> d
280
  OrderedDict({'one': 1, 'two': 2, 'three': 3})
281
  >>> d.move_to_end("one")
282
  >>> d
283
  OrderedDict({'two': 2, 'three': 3, 'one': 1})
284
  >>> d.move_to_end("three", last=False)
285
  >>> d
286
  OrderedDict({'three': 3, 'two': 2, 'one': 1})
287
  >>> d.popitem()
288
  ('one', 1)
289
  >>> d
290
  OrderedDict({'three': 3, 'two': 2})
291
  >>> d.popitem(last=False)
292
  ('three', 3)
293
  >>> d
294
  OrderedDict({'two': 2})
295
  ```
296
297
- 名前付きフィールドを持つタプル namedtuple
298
  - タプルの各値に属性名を割り当て、インデックス以外に属性名から参照もできるタプル
299
  - 関数 `namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)`
300
    - `typename` 作成する型名を指定
301
    - `field_names` タプルの要素名を指定
302
      - 要素名シーケンス
303
      - 要素名をスペースかカンマで区切った文字列
304
    - `rename` True の場合、不正な要素名を自動的に位置名 (`_1` など) に変換する
305
    - `defaults` デフォルト値のイテラブルを指定
306
    - `module` 名前付きタプルの `__module__` 属性を指定した値に設定する
307
    - **戻り値** namedtuple オブジェクト
308
309
    ```python
310
    >>> from collections import namedtuple
311
    >>> Pet = namedtuple('Pet', 'animal, name, age')  # Pet型を作成
312
    >>> seven = Pet('ferret', 'せぶん', 3)  # Pet型のインスタンスを作成
313
    >>> seven  # 「名前=値」の形式で確認できる
314
    Pet(animal='ferret', name='せぶん', age=3)
315
    >>> michiko = Pet('cat', 'ミチコ', 1)  # 別のインスタンスを作成
316
    >>> michiko
317
    Pet(animal='cat', name='ミチコ', age=1)
318
    >>> seven.age  # 属性名で値を取得
319
    3
320
    >>> seven[1]  # インデックスでもアクセスできる
321
    'せぶん'
322
    >>> animal, name, age = michiko  # アンパック代入も可能
323
    >>> animal
324
    'cat'
325
    ```
326
327
## 9.3 二分法アルゴリズムを利用する bisect
328
329
- 二分法アルゴリズムで挿入する位置を返す
330
  - `bisect_left(a, x, lo=0, hi=len(a))`
331
  - `bisect_right(a, x, lo=0, hi=len(a))`
332
  - `bisect(a, x, lo=0, hi=len(a))`
333
    - `a` ソート済みのシーケンス
334
    - `x` 挿入位置を検索する値
335
    - `lo` 検索開始位置
336
    - `hi` 検索終了位置
337
    - **戻り値** 挿入位置のインデックス
338
      - ソート済みシーケンス `a` に `x` を挿入する位置のインデックス (`a` に `x` が含まれない場合、全関数は同じ結果)
339
      - `a` に `x` が含まれる場合
340
        - `bisect_left()` は、最初の `x` の位置インデックスを返す
341
        - `bisect_right()` `bisect()` は、最後の `x` の次の位置インデックスを返す
342
343
    ```python
344
    >>> import bisect
345
    >>> seq = [0, 1, 2, 2, 3, 5]  # 昇順にソートしたリスト
346
    >>> bisect.bisect_left(seq, 4)  # 4の挿入インデックスは5
347
    5
348
    >>> bisect.bisect_right(seq, 4)  # bisect_rightでも同じ
349
    5
350
    >>> bisect.bisect_left(seq, 2)  # 最初の2の要素のインデックスを返す
351
    2
352
    >>> bisect.bisect_right(seq, 2)  # 最後の2の要素の次のインデックスを返す
353
    4
354
    ```
355
356
- ソート済みのリストに要素を挿入する
357
  - `insort_left(a, x, lo=0, hi=len(a))`
358
  - `insort_right(a, x, lo=0, hi=len(a))`
359
  - `insort(a, x, lo=0, hi=len(a))`
360
    - `a` ソート済みの **リスト** (※ `bisect()` 等と異なり、指定できるのはリストのみ)
361
    - (残り引数略:`bisect()` 等と同じ)
362
    - **戻り値** None
363
  - 第1引数のリストを変更する、破壊的操作となる
364
365
## 9.4 列挙型による定数の定義を行う enum
366
367
- 定数値を定義する
368
  - 列挙型は `enum.Enum` の派生クラスに定義する
369
370
    ```python
371
    import enum
372
    
373
    class Example(enum.Enum):
374
        名前1 = 値1
375
        名前2 = 値2
376
        名前3 = 値3
377
    ```
378
379
    ```python
380
    >>> import enum
381
    >>> class Nengo(enum.Enum):
382
    ...     SHOWA = enum.auto()
383
    ...     HEISEI = enum.auto()
384
    ...     REIWA = enum.auto()
385
    ...
386
    >>> Nengo.SHOWA     # 属性で定数取得
387
    <Nengo.SHOWA: 1>
388
    >>> Nengo["SHOWA"]  # キーで定数取得
389
    <Nengo.SHOWA: 1>
390
    >>> Nengo(2)        # 値から定数取得
391
    <Nengo.HEISEI: 2>
392
    >>> Nengo.SHOWA.name   # 定数の名前
393
    'SHOWA'
394
    >>> Nengo.SHOWA.value  # 定数の値
395
    1
396
    >>> for ng in Nengo:  # 列挙型は定数を定義順に返すイテレーターを返す
397
    ...     print(f"{ng.name} : {ng.value}")
398
    ...
399
    SHOWA : 1
400
    HEISEI : 2
401
    REIWA : 3
402
    ```
403
  
404
  - 定数同士の比較
405
406
    ```python
407
    >>> import enum
408
    >>> class Spam(enum.Enum):
409
    ...     HAM = 1
410
    ...     EGG = 2
411
    ...     BACON = 2
412
    ...
413
    >>> isinstance(Spam.HAM, Spam)  # HAM、EGG、BACONはSpam型のインスタンス
414
    True
415
    >>> Spam.HAM == Spam.HAM  # 同じ値同士の比較
416
    True
417
    >>> Spam.HAM == Spam.EGG  # 異なる値との比較
418
    False
419
    >>> Spam.EGG == Spam.BACON  # 別の名前でも値が同じなら等しい
420
    True
421
    >>> class OtherSpam(enum.Enum):
422
    ...     HAM = 1
423
    ...     EGG = 2
424
    ...     BACON = 2
425
    ...
426
    >>> Spam.HAM == OtherSpam.HAM  # 異なる列挙型の同じ値(=1)同士の比較
427
    False
428
    >>> Spam.HAM == 1  # 整数値との比較
429
    False
430
    ```
431
432
- enum は関数やメソッドの引数に使用することで「引数が特定のパターンしか受け付けない」仕様を記述できる
433
  - ただし、動的型付けにより期待以外の値が渡されることは防げないので、mypy などの静的型チェックを行うことで制限できる
434
- 列挙型の定数参照は、文字列を使用した `Nengo["SHOWA"]` の形式で可能だが、この場合文字列として定義外の定数名を指定してしまうことがあり、その場合は **KeyError** が送出される
435
436
## 9.5 データを読みやすい形で出力する pprint
437
438
- オブジェクトを整形して出力する pprint
439
  - `pprint(object, stream=None, indent=1, width=80, depth=None, compact=False, sort_dicts=True)`
440
    - `object` 出力するオブジェクト
441
    - `stream` 出力先のファイルオブジェクト (None なら sys.stdout)
442
    - `indent` ネストしたオブジェクト出力の際のインデント数
443
    - `width` 出力幅
444
    - `depth` ネストしたオブジェクト出力の際の最大レベル数 (None なら全レベル)
445
    - `compact` 出力されるシーケンスの整形方法の指定
446
      - True : 各行に width の幅に収まるだけの要素を出力
447
      - False : 1 行に 1 要素を出力
448
    - `sort_dicts` 出力される辞書の整形方法の指定
449
      - True : キーでソートして出力
450
      - False : 挿入順に出力
451
    - **戻り値** None
452
453
    ```python
454
    >>> import pprint
455
    >>> import sys
456
    >>> sys.implementation  # 1行につながっていて読みにくい
457
    namespace(name='cpython', cache_tag='cpython-39', version=sys.version_info(major=3, minor=9, micro=5, releaselevel='final', serial=0), hexversion=50922992, _multiarch='darwin')
458
    >>> pprint.pprint(sys.implementation)
459
    namespace(name='cpython',
460
              cache_tag='cpython-39',
461
              version=sys.version_info(major=3, minor=9, micro=5, releaselevel='final', serial=0),
462
              hexversion=50922992,
463
              _multiarch='darwin')
464
    ```
465
466
- オブジェクトを整形した文字列を取得する pformat
467
  - `pformat(object, indent=1, width=80, depth=None, compact=False, sort_dicts=True)`
468
    - (引数略 : `stream` が無い以外は `pprint()` と同じ)
469
    - **戻り値** 整形された文字列
470
471
    ```python
472
    >>> import pprint
473
    >>> formatted_implementation = pprint.pformat(sys.implementation)
474
    >>> print(formatted_implementation)
475
    namespace(name='cpython',
476
              cache_tag='cpython-39',
477
              version=sys.version_info(major=3, minor=9, micro=5, releaselevel='final', serial=0),
478
              hexversion=50922992,
479
              _multiarch='darwin')
480
    ```
481
482
- ログ出力関連でよく使用される
483
- pprint は pdb デバッガーの `pp` コマンドの内部でも使用されている (17.1 参照)
484
485
## 9.6 イテレーターの組み合わせで処理を組み立てる itertools
486
487
- イテラブルオブジェクトを連結する
488
  - `chain(*iterables)`
489
    - `iterables` 複数のイテラブルオブジェクト
490
    - **戻り値** イテレーター
491
492
    ```python
493
    >>> import itertools
494
    >>> lt = [0, 5, 2]
495
    >>> it = itertools.chain(lt, "ABC", range(3))
496
    >>> for elem in it:
497
    ...     elem
498
    ...
499
    0
500
    5
501
    2
502
    'A'
503
    'B'
504
    'C'
505
    0
506
    1
507
    2
508
    ```
509
510
- 連続する値をまとめる
511
  - `groupby(iterable, key=Noen)`
512
    - `iterable` イテラブルオブジェクト
513
    - `key` 比較対象値を取得する関数を指定 (省略 or None の場合、要素をそのまま比較)
514
    - **戻り値** グループ化されたイテレーター
515
      - 「長さ2のタプル」のイテレーター
516
      - 各タプルの第1要素は `iterable` の単一要素、第2要素は連続した等しい値のオブジェクトを返すイテレーター
517
518
    ```python
519
    >>> import itertools
520
    >>> for value, group in itertools.groupby('aaabbcdddaabb'):  # 同じ文字ごとにグループ化する
521
    ...     print(f'{value}: {list(group)}')  # 各要素とイテレーターをリストに変換して確認
522
    ...
523
    a: ['a', 'a', 'a']
524
    b: ['b', 'b']
525
    c: ['c']
526
    d: ['d', 'd', 'd']
527
    a: ['a', 'a']
528
    b: ['b', 'b']
529
    ```
530
531
  - key を指定した例
532
533
    ```python
534
    >>> import itertools
535
    >>> def is_odd(num):
536
    ...     return num % 2 == 1  # 奇数ならTrueを返す
537
    ...
538
    >>> numbers = [10, 20, 31, 11, 3, 4]
539
    >>> for value, group in itertools.groupby(numbers, is_odd):  # is_odd()関数の結果でグループ化
540
    ...     print(f'{value}: {list(group)}')
541
    ...
542
    False: [10, 20]
543
    True: [31, 11, 3]
544
    False: [4]
545
    ```
546
547
- イテレーターから範囲を指定して値を取得する
548
  - リスト等のスライスと同様の部分取得を提供
549
  - `islice(iterable, stop)`
550
  - `islice(iterable, start, stop[, step])`
551
    - `iterable` イテレーターオブジェクト
552
    - `stop` 値読み取りの終了位置 (None の場合は最後まで取得)
553
    - `start` 値読み取りの開始位置 (未指定 or None の場合は最初から取得)
554
    - `step` 値を読み取る際の増分 (デフォルトは 1)
555
556
    ```python
557
    >>> import itertools
558
    >>> li = list(range(10))
559
    >>> li
560
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
561
    >>> li
562
    >>> islice_object = itertools.islice(li, 5)  # リストの最初の5要素を返す
563
    >>> islice_object
564
    <itertools.islice object at 0x1089de8b0>
565
    >>> list(islice_object)  # リストに変換して確認する
566
    [0, 1, 2, 3, 4]
567
    ```
568
569
- 複数のイテラブルオブジェクトの要素からタプルを作成する
570
  - `zip(*iterables)` (組み込み関数)
571
    - `iterables` イテラブルオブジェクト
572
    - **戻り値** zipイテレーター
573
      - 指定された複数のイテラブルオブジェクから、各第 n 要素を並べたタプルを返すイテレーター
574
      - イテラブルオブジェクトの長さが異なる場合は、最短のものに揃えて終わる
575
      - Python3.10 以降では `strict` 引数が追加され、True 指定すると、長さが異なるイテラブルを指定した際に ValueError 送出となる
576
577
    ```python
578
    >>> it1 = (1, 2, 3, 4, 5)  # 長さが5のため、後ろ2つは切り捨てられる
579
    >>> it2 = ['abc', 'ABC', '123']  # 長さが3
580
    >>> for v in zip(it1, it2):  # 2つのイテラブルオブジェクトを指定
581
    ...    print(v)
582
    ...
583
    (1, 'abc')
584
    (2, 'ABC')
585
    (3, '123')
586
    ```
587
588
  - `zip_longest(*iterables, fillvalue=None)` (itertools モジュール)
589
    - `iterables` イテラブルオブジェクト
590
    - `fillvalue` イテラブルオブジェクの要素が存在しない (足りない) 場合に使用する値
591
    - **戻り値** zip_longest イテレーター
592
      - 指定された複数のイテラブルオブジェクから、各第 n 要素を並べたタプルを返すイテレーター
593
      - イテラブルオブジェクトの長さが異なる場合は、**最長のものに揃えられ** 、短いイテラブルオブジェクトの要素として `fillvalue` の値が使用される
594
595
    ```python
596
    >>> import itertools
597
    >>> for v in itertools.zip_longest('abcde', '123', 'あいうえ', fillvalue='-'):
598
    ...     print(v)
599
    ...
600
    ('a', '1', 'あ')
601
    ('b', '2', 'い')
602
    ('c', '3', 'う')
603
    ('d', '-', 'え')
604
    ('e', '-', '-')
605
    ```
606
607
- データを組み合わせたイテレーターを取得する
608
  
609
  |関数|解説|文字列 ABC 長さ 2 の場合の結果|
610
  |---|---|---|
611
  |`product(*iterables, repeat=1)`|デカルト積|AA AB AC BA BB BC CA CB CC|
612
  |`permutations(iterable, r=None)`|順列|AB AC BA BC CA CB|
613
  |`combinations(iterable, r)`|組み合わせ|AB AC BC|
614
  |`combinations_with_replacement(iterable, r)`|組み合わせ (同要素の繰り返しを含む)|AA AB AC BB BC CC|
615
616
## 9.7 ミュータブルなオブジェクトをコピーする copy
617
618
- 浅いコピーを行う
619
  - `copy()` 関数
620
  - 1 階層のみのコピーを行う
621
  - 2 階層目以降は参照がコピーされる
622
623
    ```python
624
    >>> import copy
625
    >>> values = [[0, 1], [2, 3], [4, 5]]  # もとのオブジェクトが2つの階層を持つ
626
    >>> val_cp = copy.copy(values)
627
    >>> val_cp.append([6, 7])  # コピー先のオブジェクトに値を追加
628
    >>> val_cp
629
    [[0, 1], [2, 3], [4, 5], [6, 7]]  # コピー先のオブジェクトのみに値が追加される
630
    >>> values
631
    [[0, 1], [2, 3], [4, 5]]  # コピー元オブジェクトは変更されていない
632
    >>> val_cp[1][0] = 8  # 子オブジェクト(2階層目)の値を変更してみる
633
    >>> val_cp
634
    [[0, 1], [8, 3], [4, 5], [6, 7]]  # コピー先オブジェクトが変更されている
635
    >>> values
636
    [[0, 1], [8, 3], [4, 5]]  # コピー元のオブジェクトも変更されている
637
    ```
638
639
- 深いコピーを行う
640
  - `deepcopy()` 関数
641
  - 子オブジェクトも再帰的にコピーする
642
643
    ```python
644
    >>> import copy
645
    >>> class Author:  # クラスを作成
646
    ...     def __init__(self, name, age):
647
    ...         self.name = name  # 名前
648
    ...         self.age = age  # 年齢
649
    ...
650
    >>> author1 = Author('kadowaki', 25)  # インスタンス1
651
    >>> author2 = Author('terada', 20)  # インスタンス2
652
    >>> author3 = copy.deepcopy(author1)  # deepcopy()関数でインスタンスをコピー
653
    >>> author3.name = 'takanory'
654
    >>> print(author1.name, author1.age)
655
    kadowaki 25
656
    >>> print(author2.name, author2.age)
657
    terada 20
658
    >>> print(author3.name, author3.age)
659
    takanory 25
660
    ```
661
662
- copy モジュールを使用しない浅いコピー
663
  - リストに対して全体指定となるスライスを使用
664
665
    ```python
666
    >>> values = [0, 1, 2, 3, 4, 5]
667
    >>> val_cp = values[:]  # スライスを使用してリスト全体を指定
668
    >>> val_cp
669
    [0, 1, 2, 3, 4, 5]
670
    >>> id(values)
671
    140016803582400
672
    >>> id(val_cp)
673
    140016804734336
674
    ```
675
676
  - 組み込み関数 `list()` `dict()` `set()` を使用
677
678
    ```python
679
    >>> values_list = [0, 1, 2, 3, 4, 5]  # リスト型の元データ
680
    >>> values_dict = {'key1': 'value1', 'key2': 'value2'}  # 辞書型の元データ
681
    >>> values_set = {1, 2, 3, 4, 5}  # 集合型の元データ
682
    >>> val_list_cp = list(values_list)  # list()関数を使用したコピー
683
    >>> val_dict_cp = dict(values_dict)  # dict()関数を使用したコピー
684
    >>> val_set_cp = set(values_set)  # set()関数を使用したコピー
685
    ```