Python3.8の新機能 (まとめ)

##はじめに
Python 3.5から What's Newの内容をまとめる記事を投稿してきました。

* [Python3.5の新機能](http://qiita.com/ksato9700/items/9a0aef07d90d00e67525)
* [Python3.6の新機能](https://qiita.com/ksato9700/items/ed839a6db6a671fd31e6)
* [Python3.7の新機能](https://qiita.com/ksato9700/items/35a0bdc04693b3b09757)

(2019-10-15更新)
10/14付でPython 3.8.0正式版が出ました([アナウンスメント](https://pythoninsider.blogspot.com/2019/10/python-380-is-now-available.html))。公式の変更点は[こちら](https://docs.python.org/3.8/whatsnew/3.8.html)にあります。ざっと見た限りではこれまで追ってきた変更点以外は大きなものはない模様です。私もこれからインストールして使ってみようかと思います。
（更新終わり）

最初はなかなか普及の進まなかったPython3も版を重ねてついに 3.8で、2.7で打ち止めになったPython2をこれでマイナーバージョン数でも越えることになります。利用者数も少し前の[この記事](https://opensource.com/article/18/5/numbers-python-community-trends)によると75%がPython3になっているみたいですし、名実ともにメインストリームにようやくなったという感じでしょうか。Python2のメンテナンスも今年いっぱい([PEP-373](https://www.python.org/dev/peps/pep-0373/))なので今年は更にPython3への移行が加速するでしょう。

なお、これまではWhat's Newの内容を網羅的にカバーしていましたが、レベルの違う変更点が羅列されて長くなるし、こちらも更新内容を追いかけるのが辛くなってくるので、個人的な視点で興味を持った部分を中心に書いていこうかなと思います。

そして書き始めて気がついたけど、取り上げる機能の数が減っても一つ一つに関して詳しく書いていくとあっという間に長くなってしまう。なので、長くなったものに関しては別記事に分離しこちらにはそれへのリンク　＋　小変更の紹介を書いていこうかと思います。

で、何はともあれ、開発ロードマップ([PEP-569](https://www.python.org/dev/peps/pep-0569/))。

* 3.8 開発開始: 2018-01-29 (完了)
* 3.8.0 alpha 1: 2019-01-27 -> 2019-02-03 (完了)
* 3.8.0 alpha 2: 2019-02-24 -> 2019-02-25 (完了)
* 3.8.0 alpha 3: 2019-03-24 -> 2019-03-25 (完了)
* 3.8.0 alpha 4: 2019-04-28 -> 2019-05-06 (完了)
* 3.8.0 beta 1: 2019-05-26 -> 2019-06-04 (完了:これ以降は新機能の追加なし)
* 3.8.0 beta 2: 2019-07-01 -> 2019-07-04 (完了)
* 3.8.0 beta 3: 2019-07-29 -> 2019-07-29 (完了)
* 3.8.0 beta 4: 2019-08-26 -> 2019-08-30 (完了)
* 3.8.0 candidate 1: 2019-09-30 -> 2019-10-01 (完了)
* 3.8.0 candidate 2: 2019-10-07 -> スキップ
* 3.8.0 final: 2019-10-21 ->  2019-10-14 (完了)

Finalは予定よりも早く出ましたね。こんなこと過去にあったかな？

##更新履歴
### 2019.10.15
3.8.0 finalが出ました。

### 2019.08.11
3.8.0b2が7/4、b3が7/29にリリースされました([b2 リリースアナウンスメント](https://blog.python.org/2019/07/python-380b2-is-now-available-for.html)、[b3 リリースアナウンスメント](https://blog.python.org/2019/08/python-380b3-is-now-available-for.html))

* ロードマップをアップデート
* Pickle プロトコルバージョン5について追記

### 2019.06.15
3.8.0b1が6/4にリリースされました([リリースアナウンスメント](https://blog.python.org/2019/06/python-380b1-is-now-available-for.html))。

* ロードマップをアップデート
* 位置のみパラメータの指定を追加
* f文字列の拡張

### 2019.02.05
3.8.0a1がリリースされました([リリースアナウンスメント](https://pythoninsider.blogspot.com/2019/02/python-380a1-is-now-available-for.html))。代入式の導入に関する記事へのリンクを追加しました。

### 2019.01.20
最初のバージョン。またa1が出ていないタイミングで書いています。

##注目した新機能

### 代入式の導入
→　別記事にしました: [Python3.8の新機能(2) - 代入式の導入](https://qiita.com/ksato9700/items/d37efc6982365e24f6af)
### コンパイルしたバイトコードファイルの置き場所を指定できる

→ 別記事にしました: [Python3.8の新機能(1) - コンパイルしたバイトコードファイルの置き場所を指定できる](https://qiita.com/ksato9700/items/da8df4e01b8b77293dbf)

### 位置のみパラメータの指定
これまでも位置のみ(キーワード呼び出しが出来ない)パラメータはありましたが、明示的にそれを指定する方法はありませんでした。例えば、

```
def pow(x, y, z=None):
  ...
```

と定義すると`pow(2,10)`とも書けるし`pow(y=10, x=2)`とも書けました。Python 3.8では、引数の並びの中に `'/'` を入れると後者の書き方を防止できます。例えば、

```
def pow(x, y, z=None, /):
  ...
```

と定義すると、`pow(2,10)`は今まで通り動作しますが、`pow(y=10, x=2)`は

```
TypeError: pow() got some positional-only arguments passed as keyword arguments: 'x'
```

というエラーになります。面白いのは`'/'`の位置によって動作を変えられることで、例えば

```
def pow(x, /, y, z=None):
  ...
```

と定義すると、`pow(x=2, 10)`はダメだけど、`pow(2, y=10)`は問題なく動作します。(`x`だけがpositional-onlyパラメータだから)

### f文字列の拡張
Python3.6で導入された、f文字列。文字列の前に`f`をつけると`{}`内の式を評価して埋め込んでくれます。例えば、こんな感じ。

```
>>> name = "John"
>>> age = 36
>>> f"My name is {name}. I'm {age} years old."
"My name is John. I'm 36 years old."
```

この`{}`内の式の最後に`=`を付けると評価した式も合わせて表示してくれる。

```
>>> name = "John"
>>> age = 36
>>> f"{name} {age}"
'John 36'
>>> f"{name=} {age=}"
"name='John' age=36"
```

もちろん、複雑な式でもOK。

```
>>> x = 3
>>> f'{x*9+15}'
'42'
>>> f'{x*9+15=}'
'x*9+15=42'
```

デバッグ時にサクッと変数の値を確認したい時に便利そう。

### Pickleプロトコルバージョン5
→　別記事にしました: [Python3.8の新機能(3) - Pickleの改良](https://qiita.com/ksato9700/items/01d5f5fbcfbb973256dd)

##その他の言語の変更

####`finally`の中で`continue`が使えるようになる
これまでは

```python
while True:
  try:
    pass
  finally:
    continue
```
というのを実行しようとすると

```
SyntaxError: 'continue' not supported inside 'finally' clause
```
というエラーが出ていました。実装上の都合でそうなっていたらしいのですが、3.8からはその制約が外れます。が、実質的にこれが必要なケースが思いつかない...

#### int型に`as_integer_ratio()`メソッド追加
これまでfloat型には`as_integer_ratio()`というメソッドがあり、

```python
>>> (3.5).as_integer_ratio()
(7, 2)
```

と浮動小数点数を`f = a/b`が成り立つ2つの整数のペアを返してくれます。このメソッドをint型にも導入するということですが、`(i, 1)`を返すだけのようです。一見意味がなさそうですが、[mypy](http://mypy-lang.org/)のような静的型付拡張されたPythonでfloat型とのint型を統一的に扱うためにこれが必要とのこと。

####辞書型(dict)でreversedが使えるようになった
3.7で辞書型の順序保証がされるようになった(入力順に並ぶ)ので当然の帰結ですが、辞書型で`reversed()`が使えるようになりました。逆に3.7でなぜ実現されなかったのかが不思議ですが、これまでは `TypeError: 'dict' object is not reversible`というエラーが出ていました。3.8ではこんな感じで普通に使えます。

```
>>> d = {'a': 1, 'b': 2}
>>> print(list(reversed(d)))
['b', 'a']
```

##モジュールの改善

####json.tool
個人的には結構良いなと思った拡張です。

jsonを見やすく表示するツールとして[jq](https://stedolan.github.io/jq/)などがありますが、自分としてよく使うのが、`python -m json.tool < <jsonfile>`。でもこれは入力が一つのjsonオブジェクトであることを想定しているので、複数のjsonオブジェクトを含むファイルを入力すると2つ目の頭を呼んだところでエラーになります。

それが、3.8から`--json-lines`というオプションが導入され、1行に1 jsonオブジェクトというフォーマットであれば読めるようになりました。

例えばこのようなjsonファイルが有る場合

```
{"city": "tokyo", "temperature": 6}
{"city": "osaka", "temperature": 5}
{"city": "taipei", "temperature": 17}
```

3.7 ではエラーになります。

```shell
$ python -m json.tool < a.json
Extra data: line 2 column 1 (char 36)
```

一方で3.8ではこのようになります。

```shell
$ python -m json.tool --json-lines < a.json
{
    "city": "tokyo",
    "temperature": 6
}
{
    "city": "osaka",
    "temperature": 5
}
{
    "city": "taipei",
    "temperature": 17
}
```
願わくば、jqみたいに1行 1jsonではなくても複数オブジェクトを読んでくれれば良いのになと思うのですが、それはそれできっと問題があるのでしょう。

##最適化
####ファイル・ディレクトリコピーの高速化
`shutil.copyfile()`, `shutil.copy()`, `shutil.copy2()`, `shutil.copytree()` `shutil.move()`がそれぞれのプラットフォームで最適なシステムコールを使って実現される様になったとのこと。結果として、Linuxで26%、 macOSで50%, Windowsで40%速くなったとの計測結果も出ているらしいです。

##まとめ
Python 3.8の変更点についてまとめてみました。b1が出て機能追加はもうありませんが、まだドキュメント化が終わっていない機能があるみたいなので、それも出てきたらまた書いてみたいと思います。