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

## 本記事について

以下のオデッセイ開催のpython基礎認定試験を今度受験するにあたり
勉強して上で学んだことを記事でアウトプットしていく。
前回記載の記事が長くなっていたのでパート2で分けていく

https://cbt.odyssey-com.co.jp/pythonic-exam.html

### map関数とは
知らない！
そんな関数あったことすら知らなかったがpythonチュートリアルに

```python
squares = list(map(lambda x: x**2, range(10)))
```
そしてこれと等価なのが

```python
squares = [x ** 2 for x in range(10)]
```

とか書いてある。
後者はrangeで0~9までループ回して結果を冪乗するというプログラムなのだろう
でも前者の説明が全くないのである。流石オライリー！
なのでmap関数をまぬあるで調べてみる

https://docs.python.org/ja/3/library/functions.html#map

>map(function, iterable, ...)
>function を、結果を返しながら iterable の全ての要素に適用するイテレータを返します。追加の iterable 引数が渡されたなら、 function はその数だけの引数を取らなければならず、全てのイテラブルから並行して取られた要素に適用されます。複数のイテラブルが与えられたら、このイテレータはその中の最短のイテラブルが尽きた時点で止まります。関数の入力がすでに引数タプルに配置されている場合は、 itertools.starmap() を参照してください。


ということでmap関数は
`map(function, iterable, ...)`
なので先の
`squares = list(map(lambda x: x**2, range(10)))`
についてはrange(10)のイテラブルを引数としてlambda x: x**2を実行するとなる。
よって

`map(lambda x: x**2, range(10))`

についてはrangeで0~9まで抜き出した値がlambdaに都度代入される
その上でその結果をlist()に渡しているので結果としてはx**2をリストに入れている

`list([0, 1, 4, 9, 16, 25, 36, 49, 64, 81])`

故に実行結果としては以下のように0~9の冪乗のリストが出来上がる

```python:実行結果
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
```


### リスト内包にフィルターをかける方法

```python
[(x, y) for x in [1, 2, 3 ] for y in [3, 1, 4] if x != y]
```

パッと何をやっているのか分かりにくいが、一番大事なのは
`if x != y`
の部分であるつまりxとyがnotイコールだったら最初のタプルに値を入れて良い
というコードである。つまり出力結果にフィルターをかけているのである。
あとはforの二重ループなので、以下のような動きとなる

>
x=1, y=3 x != yなのでリストにappend -> (1, 3)
x=1 ,y=1 x == yなのでリストにappendされない
x=1 ,y=4 x != yなのでリストにappend -> (1, 4)
>
x=2, y=3 x != yなのでリストにappend -> (2, 3)
x=2 ,y=1 x != yなのでリストにappend -> (2, 1)
x=2 ,y=4 x != yなのでリストにappend -> (2, 4)
>
x=3, y=3 x == yなのでリストにappendされない
x=3 ,y=1 x != yなのでリストにappend -> (3, 1)
x=3 ,y=4 x != yなのでリストにappend -> (3, 4)

```python:出力結果
[(1, 3)(1, 4)(2, 3)(2, 1)(2, 4)(3, 1)(3, 4)]
```

同じような感じでリストから50以上の値を抜いてリストを作りたいという時には以下のようにやってやれば良い。とっても簡単である

```python
l = [3, 20, 50 ,15, 25, -3, 100 , 65]
print([i for i in l if i >= 50])
```

```python:出力結果
[50, 100, 65]
```

### zip関数
複数リストの結合をしたいときに使うのがzip関数
具体的には以下のような形

```python
data = [
    ['Yamada', 'Tanaka', 'Kojima'],
    [25, 34, 29],
    ['Tokyo', 'Ibaragi', Kagoshima]
]
profile = [i for i in zip(*data)]
print(profile)
```

```python:出力結果
[('Yamada', 25, 'Tokyo'), ('Tanaka', 34, 'Ibaragi'), ('Kojima', 29, 'Kanagawa')]
```

ポイントは*dataで二重リストをアンパッキングしてzip関数に渡しているところである
※ただしお互いのリストの数が同じでないと出力されないので注意

### タプルのアンパッキング
例えば以下のように最後にカンマをつけるとタプルになる
タプルはイミュータブルなので変更はできないがミュータブルな要素をタプルに入れることはできる

```python
a = 'Hello',
print(a)
```
```python:実行結果
('Hello',)
```

ミュータブルなので勿論range関数とかも入れられる

```python
a = range(3),
print(a)
```

```python:実行結果
(range(0, 3),)
```

これをそのまま複数の変数に入れられたりする

```python
b, c ,d = a[0]
print(b, c, d)
```

```python:実行結果
0 1 2
```

これを **シーケンスアンパッキング！！！** と呼ぶ
※実に厨二心をくすぐるかっこいい言葉だと思う。必殺技として叫びつつ黒歴史ノートに是非とも記載してほしい。

###集合

重複除去で主に使うと思うのが集合である
複数ある場合は{}の中に重複除去したいものを入れる
重複除去した結果はsorted等でソートしてリストで返してやるのが良いと思う

```python
print(sorted({'Apple', 'aPplE', 'apple', 'Apple'}))
```

```python:実行結果
['Apple', 'aPplE', 'apple']
```

一つだけの場合はset()を使うと良い

```python
print(sorted(set('abcabcabc')))
```

```python:実行結果
['a', 'b', 'c']
```

###辞書内包表記

辞書内包表記は大体タプルの時と一緒なので以下のようにリストから辞書にコンバートできる

```python
sample_list = ['Apple', 'Orange', 'banana']
fruits_dict = {k: v for k ,v in enumerate(sample_list)}
print(fruits_dict)
```

```python:実行結果
{0: 'Apple', 1: 'Orange', 2: 'banana'}
```

アルファベットのa-zまでに0からの数字を入れた辞書を作りたいなんて場合は次のようにしてやれば良い。※a-zまで書くのが面倒なのでこういう時はstringモジュールを使うとすごい楽ができる

```python
import string
alphabet = {v: i for i, v in enumerate(string.ascii_uppercase[:26])}
print(alphabet)
```

```python:出力結果
{'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7, 'I': 8, 'J': 9, 'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14, 'P': 15, 'Q': 16, 'R': 17, 'S': 18, 'T': 19, 'U': 20, 'V': 21, 'W': 22, 'X': 23, 'Y': 24, 'Z': 25}
```

###シーケンスの比較

以下の判定がどのように行われるかをチェックする

```python:問題
(1, 2, 3) < (1, 2, 4)
[1, 2, 3] < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4) <(1, 2, 4)
(1, 2) < (1, 2, -1)
(1, 2, 3) < (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab')) < (1, 2, ('abc', 'a'), 4)
```

実際はこのような形で判定が行われる

```python:問題
(1, 2, 3) < (1, 2, 4)
[1, 2, 3 < [1, 2, 4]
```

```python:実行結果
True
True
※リストもタプルも結果は一緒
```

```:判定内容
※リストもタプルも判定内容は一緒
1 < 1 False
2 < 2 False
3 < 4 True
```

```python:問題
'ABC' < 'C' < 'Pascal' < 'Python'
```

```python:実行結果
True
```

```python:判定内容
A < C True

Cはアルファベット順で判定される
※厳密にはアルファベットのユニコード値で判定される
ord('A') #65
ord('C') #67
なので　A < C はCが大きいとなる

- 20210614追記
アルファベットは大文字より小文字のが大きいと判断される

例えば

'H' < 'E' は False
だが
'H' < 'e' は True
となる
```

```python:問題
(1, 2, 3, 4) <(1, 2, 4)
```

```python:実行結果
True
```

```判定内容
1 < 1 False
2 < 2 False
3 < 4 True
```

```python:問題
(1, 2) < (1, 2, -1)
```

```python:実行結果
True
```

```判定内容
1 < 1 False
2 < 2 False
残りの要素数が多い方が大となるので-1がある右側のtupleが大
よって結果としてはTrue
```

```python:問題
(1, 2, 3) < (1.0, 2.0, 3.0)
```

```python:実行結果
False
```

```:判定内容
1 < 1.0 False
2 < 2.0 False
3 < 3.0 False
```

```python:問題
(1, 2, ('aa', 'ab')) < (1, 2, ('abc', 'a'), 4)
```

```python:実行結果
True
```

```:判定内容
1 < 1 False
2 < 2 False

('aa', 'ab') < ('abc', 'a')
の比較なので
'aa' < 'abc'をまず考える

a < a False
a < b True
```

###文字列出力関数str, repr, eval, printの違い

```python
from datetime import date

a = type(date.today()), date.today()
b = type(print(date.today())), print(date.today())
c = type(repr(date.today())), repr(date.today())
d = type(eval("date.today()")), eval("date.today()")
e = type(str(date.today())),str(date.today()) 
print('date: ', a, '\nprint: ', b,'\nrepr: ', c,'\neval: ', d,'\nstr: ', e)
```

```python:実行結果
date:  (<class 'datetime.date'>, datetime.date(2021, 6, 14))
print:  (<class 'NoneType'>, None)
repr:  (<class 'str'>, 'datetime.date(2021, 6, 14)')
eval:  (<class 'datetime.date'>, datetime.date(2021, 6, 14))
str:  (<class 'str'>, '2021-06-14')
```

なんとなくお分かり頂けただろうか？
1番上はdate関数をtodayで出力した場合、単純にdatetime.date型で出力されている
2番目はprintでその出力結果を出した場合、print文は基本Noneを戻り値として返すことがわかる
3番目がrepr関数でdate関数をtodayで出力した場合、reprは文字列strとして返すがstrとの違いとして実行結果をそのまま返すのか、実行結果前の式の状態で返すのかの違いがある模様
こちらは5番目の出力のstrを見るとわかる
5番目のstrでdate関数をtodayで出力した場合、出力結果として加工した後の'2021-06-14'という日付を返してくれるのでそのまま他の処理で使えるがreprの場合は関数を実行したけ結果を'datetime.date(2021, 6, 14)'のような型として返してくるのでそのままでは使えない
使うためにはprintとかstrで別途実行してやる必要がある

次のような形である

`print(datetime.date(2021, 6, 14))`
`str(datetime.date(2021, 6, 14))`

という形でなんとなく違いはわかっていただけたかと思う