pythonエンジニア認定試験受験記①

## 目的

pythonの実力を試したいなあと色々調べていたところオデッセイというところがやってるpythonの民間資格があることを知り受験を決意
https://cbt.odyssey-com.co.jp/pythonic-exam.html

なるほど公式ドキュメントはpython3のオライリーのチュートリアル本なのね〜ふむふむ

・・・あの読みにくいやつね！

というわけでせっかく勉強するので勉強した内容を残しておこうと思う。

###ミュータブルとイミュータブルについて

文字列というのは基本イミュータブルなので書き換えができない
例えば以下のように最後の文字をTAMAOではなくTAMAEに変えたいなあという時
文字列はそのままでは置換できない

```python:実行結果
words = 'My_Name_is_TAMAO.'
words[-2] = 'E'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-81-18312fda4fb1> in <module>
       1 words = 'My_Name_is_TAMAO.'
----> 2 words[-2] = 'E'
 
TypeError: 'str' object does not support item assignment
```
これを置換するには単純にreplaceを使って置換する方法が一つ

```python
words = 'My_Name_is_TAMAO.'
words.replace('TAMAO',TAMAE')
```

文字列を一旦空のリストに入れてやってから必要なところだけ置換して最終的にそれを連結させる方法がある

```python
words = 'My_Name_is_TAMAO.'

word_list = []
for word in words:
    word_list.append(word)
word_list[-2] = 'E'
print(''.join(word_list))
```

いずれも出力は以下の通り

```python:実行結果
'My_Name_is_TAMAE.'
```

最初の方のが簡単なのかなとは思うが、リストはミュータブルなので変更できるというお話である。

※追記1 
因みにこういう時は先のコードをリスト内包表記で書くともっと簡単に書けることを思い出した。

```python
words = 'My_Name_is_TAMAO.'

word_list = [word for word in words]
word_list[-2] = 'E'
print(''.join(word_list))
```

※追記2
そもそもリストに文字列を入れてやればforで回す必要すらないのでは？とユーザさんより教えていただく(@shiracamusさんどうもありがとうございます。)

```python
words = 'My_Name_is_TAMAO.'

word_list = list(words)
word_list[-2] = 'E'
print(''.join(word_list))
```

確かに！！！また一つ賢くなってしまった・・・

そもそもスライシング必要なところだけ抜き出してくっつけるだけで良いのではという話も頂きました。多分これが一番シンプルと思われる。文字列は編集できないだけで必要な箇所だけ抜き出して文字列同士を連結できないわけではない為

```python
words = 'My_Name_is_TAMAO.'
print(words[:-2] +'E.')
```

### ラムダ式
正直これを書いている間もlambdaというのがよくわからない。
というかわからないのであまり私自身が使わないが多分使った方が楽な時もあるのだろう

例えばリスト内包表記とかは1~10までを足し合わせた結果を出力したい場合、こんな風にかけて便利とかはなんとなくわかるのだ

```python
sum(i for i in range(11))
```

因みに以下のようにリストをsum関数に入れても結果は同じだが、そもそもsum関数はリストのようなイテラブル受け取れるイテレータなのでわざわざ先のようにリストを書かなくても良いという話だ

```python
sum([i for i in range(11)])
```

話が逸れたがlambdaも似たようなもので関数を一々定義しなくても簡単にワンライナーでかけるというものだ。(そもそもlambda自体が無名関数と呼ばれているらしい)

例えば円の面積求める関数を書いたとして

```python
import math
def Circumference(r):
    return round(math.pow(r, 2) * math.pi, 2)

Circumference(7)
```

実行結果は以下のようになる

```python:実行結果
153.94
```

これと同じことをlambdaで書きたい場合はこうなる

```python
import math
Circumference = lambda r: round(math.pow(r, 2) * math.pi, 2)
Circumference(7)
```

※実行結果は同じ

つまりlambdaで書くと関数のreturn文を省略して

>lambda 引数: リターンを省略した戻り値

という形になるということなのである
・・・なのだがいつも聞くとああそれそれと思うが直感的に使えるようには中々ならない
慣れなのだろうか

- 20210616追記

lambda利用のパターンとして多重リストをソートしたいというパターンが多いように思う。
例えば以下のコードを例にあげる

```python
company_code = [(2, 'USA'), (1, 'JPN'), (5, 'BRA'), (3, 'CHN'), (4, 'TWN')]
print(sorted(company_code, key=lambda company_code: company_code[0]))
print(sorted(company_code, key=lambda company_code: company_code[1]))
```

この場合は実行結果は以下の通りになる

```python:実行結果
[(1, 'JPN'), (2, 'USA'), (3, 'CHN'), (4, 'TWN'), (5, 'BRA')]
[(5, 'BRA'), (3, 'CHN'), (1, 'JPN'), (4, 'TWN'), (2, 'USA')]
```

上から順にsortedの関数で処理をしているが
sortedの実際の処理は
第一引数でソートを行いたい対象のリストを
第二引数で並び替えを行いたい条件をkeyで指定する
以下のlambda関数は`company_code`というリストに対して`return company_code[0]`を返すという処理なので今回の場合は最初の数字の部分で並び替えをせよという指定となる

`key=lambda company_code: company_code[0]`

company_code[1]と指定した場合は2番目の要素(今回だと国名コード)でアルファベット順に並べ替えをせよという指定となる

もちろんリストなのでリストの組み込み関数であるsortでも実行可能だ

```python
company_code = [(2, 'USA'), (1, 'JPN'), (5, 'BRA'), (3, 'CHN'), (4, 'TWN')]
company_code.sort(key=lambda company_code: company_code[0])
print(company_code)
company_code.sort(key=lambda company_code: company_code[1])
print(company_code)
```

ただしlistの組み込みのsortを使った場合は元のlistを書き換えてしまうため、
事前に元のリストを残したい場合は次のようにリストのコピーを別名で保存した後書き換えたほうが良いと思われる
※sortedを使う場合は元のリストを書き換えたりしないので気にしなくて良い

```python
company_code = [(2, 'USA'), (1, 'JPN'), (5, 'BRA'), (3, 'CHN'), (4, 'TWN')]
origin_code =company_code.copy()
```

最終的にsortを使う場合は次のようなコードになるだろうか

```python
company_code = [(2, 'USA'), (1, 'JPN'), (5, 'BRA'), (3, 'CHN'), (4, 'TWN')]
origin_code =company_code.copy()
company_code.sort(key=lambda company_code: company_code[0])
print(company_code)
company_code.sort(key=lambda company_code: company_code[1])
print(company_code)
print(origin_code)
```

```python:実行結果
[(1, 'JPN'), (2, 'USA'), (3, 'CHN'), (4, 'TWN'), (5, 'BRA')]
[(5, 'BRA'), (3, 'CHN'), (1, 'JPN'), (4, 'TWN'), (2, 'USA')]
[(2, 'USA'), (1, 'JPN'), (5, 'BRA'), (3, 'CHN'), (4, 'TWN')]
```

###イテレータ
イテレータとはイテラブルを受け取る側のことである。多分
イテラブルとはリストやタプルや集合や辞書とか要は値を複数持っているものなのである
今回はそのイテレータを実際に実装してみようということでやってみる

因みにイテレータのドキュメントは以下の通り
https://docs.python.org/ja/3/library/functions.html?highlight=iter#iter


イテレータはiter関数で定義するがnextでイテレータの要素を順に呼び出し要素がなくなった時点でStopIterationという例外を送出する

実際この処理をtry ~ exceptで記載すると次のようになる

```python
l = ('123','456','789')
l = iter(l)

while True:
    try:
        print(next(l))
    except StopIteration:
        break
```

```:実行結果
123
456
789
```

同じようなものに(というかほぼ一緒)ジェネレータというものがある
これは関数の中でyieldという戻り値をreturnの代わりに定義すると
関数を呼び出した際にnextで順にyieldで定義したものを呼べるというものである

具体的には以下の通り

```python
def number_list():
    yield '123'
    yield '345'
    yield '789'

n_list=number_list()

print(next(n_list))
print(next(n_list))
print(next(n_list))
```

実行結果はイテレータの時と同じ