Python3.8の新機能を一部触ってみた①

# はじめに
弱々Pythonエンジニアがpython3.8の新機能を一部触ってみました。
よくわからないものはすっ飛ばしてます（ビルドだプロファイルだなんだのは飛ばしてます）。
詳しくは[公式ドキュメント参照](https://docs.python.org/ja/3/whatsnew/3.8.html)


# セイウチ演算子
>大きな構文の一部として、変数に値を割り当てる新しい構文 := が追加されました。 この構文は セイウチの目と牙 に似ているため、「セイウチ演算子」の愛称で知られています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357470/a43c0f97-a343-9a26-4920-517f01732294.png)
<font color="red">セイウチ演算子カワイイ</font>


```py
array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# python3.8以前
# n = len(array) > 10
# if n:
#     print(f"List is too long ({n} elements, expected <= 10)")

# python3.8
if n := len(array) > 10:
    print(f"List is too long ({n} elements, expected <= 10)")
```
出力結果

```
List is too long (True elements, expected <= 10)
```
判定と一緒に変数宣言できる感じ??

while文や内包表記でも使用できるので詳しくは[公式ドキュメント参照](https://docs.python.org/ja/3/whatsnew/3.8.html)

# 位置専用引数
>関数の引数が位置引数として指定されねばならずキーワード引数としては指定できないことを表す、新しい構文 / を追加しました。この構文は、help() で Larry Hasting の Argument Clinic でアノテートした C関数を表示したときと同じ表記です。

### 正しい関数呼び出し
```py
def profile(first_name, last_name, /, gender, age, *, zip_code, address):
    print(f'\
        姓：{first_name}\n\
        名：{last_name}\n\
        性：{gender}\n\
        歳：{age}\n\
        〒：{zip_code}\n\
        住:{address}')

profile('鈴木', '一郎', '男', 99, zip_code='000-0000', address='宇宙')  # OK
profile('鈴木', '一郎', '男', age=99, zip_code='000-0000', address='宇宙')  # OK
profile('鈴木', '一郎', gender='男', age=99, zip_code='000-0000', address='宇宙')  # OK
```
引数は左から順に、
位置引数：`first_name`, `last_name`
位置引数でもあり、キーワード引数でもある：`gender`, `age`
キーワード引数：`zip_code`, `address`

### 誤った関数呼び出し
```py
profile('鈴木', '一郎', gender='男', 99, zip_code='000-0000', address='宇宙')  # SyntaxError: positional argument follows keyword argument
profile('鈴木', '一郎', age=99, '男', zip_code='000-0000', address='宇宙')  # SyntaxError: positional argument follows keyword argument
profile('鈴木', '一郎', '男', 99, '000-0000', address='宇宙')  # TypeError: profile() takes 4 positional arguments but 5 positional arguments (and 1 keyword-only argument) were given
profile('鈴木', '一郎', '男', 99, zip_code='000-0000', '宇宙')  # SyntaxError: positional argument follows keyword argument
```

### キーワード引数としての利用を排除
`/`より前の引数は位置引数になるので、python3.8以前ではできていた以下のような呼び出し方を排除できるようになりました。
位置引数として強制させることができるので<font color="red">遠慮なく引数名を変更できますね!!!</red>

```py
# python3.8以前は位置引数としても、キーワード引数としても利用できた。
def profile(first_name, last_name):
    print(f'{first_name} {last_name}')
profile2(first_name='吉田', last_name='花子')  # 吉田 花子
...
..
.
# python3.8位置専用引数 / を使用
def profile(first_name, last_name, /):
    print(f'{first_name} {last_name}')
profile2(first_name='吉田', last_name='花子')  # TypeError: profile2() got some positional-only arguments passed as keyword arguments: 'first_name, last_name'
```

### 任意のキーワード引数を受け取るこんな書き方もできるよ。
```py
def profile(first_name, last_name, /, **kwargs):
    print(f'{first_name} {last_name} {kwargs}')
profile('吉田', '花子', first_name='鈴木', last_name='一郎')  # 吉田 花子 {'first_name': '鈴木', 'last_name': '一郎'}
```
>これにより、任意のキーワード引数を受け取る関数やメソッドを定義するのが、ぐんとシンプルになります。

と公式にありますが、弱々エンジニアなので旨味がわかってません。

# キャッシュファイルを別の場所に格納する機能
>暗黙のバイトコードキャッシュは、デフォルトでは各ソースコードのディレクトリ内の __pycache__ サブディレクトリを使用しますが、新たに追加された環境変数 PYTHONPYCACHEPREFIX （またはコマンドラインオプション -X pycache_prefix) を指定すると、ソースコードと分離されたディレクトリツリーに格納されるようになります。

>キャッシュの場所は sys.pycache_prefix で参照できます（__pycache__ を使用する場合は:const:None になります）。

うまく検証できなかったので、とりあえずスルー。。。

# フォーマット済み文字列リテラル(f-string)で「=」指定子が有効になった
```py
string = 'エフストリング'

# python3.8.0 以前
print(f'{string}')  # エフストリング

# python3.8.0
print(f'{string=}')  # string='エフストリング'
```

デバッグで役立ちそう（小並感）

# dict と dictview は、reversed() を使って逆順でイテレートすることが可能に
python3.7までは、`dict`を`reversed`で逆順にしようとすると`TypeError: 'dict' object is not reversible`になってしまいます。

```py
# python3.8.0 以前
dictionary = {'a': 1, 'b': 2, 'c': 3}

for d in reversed(dictionary):
    print(d)

>> TypeError: 'dict' object is not reversible
```

python3.8からはそれが可能になりました!

```py
# python3.8.0 以前
dictionary = {'a': 1, 'b': 2, 'c': 3}

for d in reversed(dictionary):
    print(d)

>> c
>> b
>> a
```

# collections.namedtuple() の _asdict() メソッドがdictを返すようになった
そもそもnamedtupleって何ぞやって方は[こちら](https://qiita.com/Seny/items/add4d03876f505442136#%E3%81%A9%E3%82%93%E3%81%AA%E3%81%A8%E3%81%8D%E3%81%ABnamedtuple%E3%82%92%E4%BD%BF%E3%81%86%E3%81%8B)にわかりやすく解説があります!!!

python3.8以前は`OrderedDict `を返すようになっていました。

```py
import collections
Human = collections.namedtuple('Human', ['first_name', 'last_name', 'age'])
human = Human('yamada', 'taro', 25)
print(human._asdict())

>> OrderedDict([('first_name', 'yamada'), ('last_name', 'taro'), ('age', 25)])
```

python3.8ではDictを返してくれるようになりました!

```py
import collections
from collections import OrderedDict

Human = collections.namedtuple('Human', ['first_name', 'last_name', 'age'])
human = Human('yamada', 'taro', 25)
print(human._asdict())
# print(OrderedDict(human._asdict()))  # 3.8でもOrderedDict特有の機能を使いたい場合はキャストして使用することが推奨されています。

>> {'first_name': 'yamada', 'last_name': 'taro', 'age': 25}
```

# csv.DictReaderもdictを返すようになった
> この変更によって、順序を保ちながら、より高速にかつメモリの使用量を減らして実行できます。

らしいです。やったぜ。

# datetimeモジュールに2つメソッドが追加
>ISO の規定に沿った年、週番号、曜日によって、date や datetime オブジェクトを作成するメソッド datetime.date.fromisocalendar() と datetime.datetime.fromisocalendar() が追加されました; これは、各クラスの isocalendar メソッドの逆を行うものです。

なんのこっちゃ。
出力してみた。

```py
from datetime import datetime

date = datetime.now()  # 2020-03-06(時分秒省略)
print(date.isocalendar())  # (2020, 10, 5)
print(date.fromisocalendar(2020, 4, 1))  # 2020-01-20 00:00:00
print(datetime.fromisocalendar(2020, 4, 1))  # 2020-01-20 00:00:00
```

### まずは既存のisocalendar()
<font color="red">`2020-03-06は`2020年　の　10週目　金(5)曜日</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357470/7a22a9d4-1419-baf3-d2de-0084f7daec9f.png)

### 3.8で追加されたfromisocalendar
`print(date.fromisocalendar(2020, 4, 1))  # 2020-01-20 00:00:00`
引数の詳細は左から順に`西暦`, `週`, `曜日`

<font color="red">``2020年　の　4週目　月(1)曜日</font>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357470/78dee5d9-bf31-f28f-e7ff-692a006986a4.png)

# ①は一旦ここまで
疲れたのでこれにて終了。
②もそのうち出します。(自己満)