Python 3.9 のリリース予定日である2020年10月05日が間近に迫ってきました。
から、Python3.9の主要な新機能を紹介します。
辞書のマージ演算子¶
2つの辞書オブジェクトを|
演算子で併合して、 一つの新しい辞書オブジェクトを作成できるようになりました。
2つの辞書オブジェクトの和から、新しい辞書オブジェクトを作成します。
print({1:'A'} | {2:'B'})
どうせ導入するなら、もっと昔から導入されていても良かった感じの機能ですね。
左項と右項の辞書に同じキー値があると、右項のキーと値が結果の辞書に追加されます。
次の例では、キー値 2
の要素が両方の項に存在しますが、右項の 2:'う'
のみが登録されます。
print({1:'あ', 2: 'い'} | {2:'う'})
リストの結合は +
演算子で、
[1,2,3] + [4,5,6]
と書くのに、辞書の場合は |
なの? と不思議に思う人もいるかも知れません。辞書の場合は、リストと違って単純な結合ではなく、2つの集合の和集合をとるため、 |
を利用しています。
同じようなパターンでは、set
型同士の演算も、+
ではなく |
で和集合を作ります。
print({1, 2, 3} | {2, 3, 4})
|= 代入文¶
|=
演算子で、左辺の辞書に右辺の辞書を追加する事もできます。
d1 = {1: 'A'}
d1 |= {2: 'B'}
print(d1)
|=
演算子で要素を追加する場合は、右辺にキーと値のシーケンスも指定できます。
d1 = {1: 'A'}
d1 |= [(2, 'B'), (3, 'C')]
print(d1)
これは、d1.update([(2, 'B'), (3, 'C')])
を実行した場合と同じです。
|
演算子の場合は、辞書オブジェクト同士しかサポートしていません。
{1: 'A'} | [(2, 'B'), (3, 'C')]
リストオブジェクトの場合も、+
演算子はリスト同士の加算しかできませんが、+=
では右辺にタプルやイテレータを指定できます。
list1 = []
list1 += (i for i in range(5))
print(list1)
s = " hello "
print(repr(s))
print(repr(s.lstrip()))
しかし、空白以外の、固定パターンの文字列を取り除くメソッドは存在しませんでした。
Githubでは、コミットログなどに GH-xxxxx
のように決まったパターンを記述すると、自動的にIssueへのリンクに変換するようになっています。PythonでこのパターンからIssue番号だけを取り出す処理を書く場合、次のように書く必要がありました。
text = "GH-1234"
if text.startswith("GH-"):
text = text[3:]
print(text)
よくあるパターンですが、比較する文字列と文字列の長さが別々の場所に出現して、コードの修正時にバグを作りそうなコードです。
Python3.9 からは、この処理は次のように書けます。
text = "GH-1234"
text = text.removeprefix("GH-")
print(text)
文字列の末尾から一致した文字列を削除するときは、 removesuffix()
を使います。【大安売り!】
のような文字列から、前後の 【】
を取り除く場合は、次のように書けます。
print("【大安売り!】".removeprefix("【").removesuffix("】"))
組み込みGeneric型¶
Pythonの型ヒント では、整数型のみを要素とするリストオブジェクトは、typing.List を使って、
from typing import List
intlist: List[int] = []
のように記述します。
List[int]
は、List
への引数として、要素の型である int
を指定しています。typing.List
のように、[]
で引数として別の型を指定できる型を、Generic型 といいます。
型 といっても、typing.List[int]
は list
のような本来の 型 ではなく、呼び出してもオブジェクトのインスタンスは作れませんし、isinstance()
で型チェックにも使用できません。純粋に、mypy などの型チェッカーが利用する注釈でしかありません。
list
型とtyping.List
のような、本当の型と注釈用の型定義が別々に存在するのは、決して好ましいことではありません。Pythonのclass
文で定義する型は Generic型
として定義できるようになっているのですが、list
や defaultdict
のような、PythonのAPIを使ってC言語で開発された、いわゆる 組み込み型 には、型アノテーションを使う方法がありません。型アノテーションはPython言語のソースコードをチェックするための記述で、Pythonの実行エンジンは、型アノテーションを使用しないのです。このため、list
型の型定義は、typing.List
で別に定義されています。
これまで、実際の型定義と型のアノテーションは別々のモジュールで定義されているため、型アノテーションを利用するソースコードでは、
from typing import (
Any,
Callable,
DefaultDict,
Dict,
Iterator,
List,
NamedTuple,
Optional,
Sequence,
Set,
Tuple,
Type,
)
のように、大量の import
の記述が必要なっており、非常に不便です。特に、defaultdict などは、collection.defaultdict
の typing.DefaultDict
の両方に存在し、とてもわかりにくい状態になっていました。
そこで、Python3.9からは list
や DefaultDict
などの組み込み型を修正し、[]
演算子をサポートして、型を指定できるようになりました。これまで、
from typing import List
intlist: List[int] = [1,2,3]
と書いていたコードは、組み込み型 list
を使って、
intlist: list[int] = [1,2,3]
と書けるようになりました。
PEP 585では、list
などの型オブジェクトの特殊メソッド __class_getitem__ が、適切なGeneric型を返すように修正されました。
list[int]
list[int]
は、types.GenericAlias
型のオブジェクトで、従来の typing.List
型の typing._GenericAlias
とは異なっています。typing.List
は、呼び出してリストオブジェクトのインスタンスを生成できませんが、list[int]
は、Python言語で定義したジェネリック型のように、インスタンスを生成できます。
# list型の呼び出し -> リストオブジェクトが生成される
list([1,2,3])
# typing.List型の呼び出し -> エラー
from typing import List
List([1,2,3])
# list[int]型の呼び出し -> リストオブジェクトが生成される
list[int]([1,2,3])
この機能追加により、typing
モジュールの List
や Dict
などは deprecateされました。Python3.9リリース後、5年後に削除される予定になっていますので、早めの移行を心がけるようにしましょう。
zoneinfoモジュール¶
PEP 615 -- Support for the IANA Time Zone Database in the Standard Library
これまで、Pythonの 日付型 では、タイムゾーンの指定方法として、Asia/Tokyo
や Japan
などの、いわゆる タイムゾーン名 はサポートされていませんでした。タイムゾーン名を使用する場合には、サードパーティライブラリの dateutil などが使われています。
Python3.9では zoneinfo
モジュールが追加され、標準ライブラリだけでタイムゾーン名を利用してタイムゾーンを指定できるようになりました。
from datetime import datetime
from zoneinfo import ZoneInfo
tokyo = ZoneInfo("Asia/Tokyo") # タイムゾーン情報を取得
now = datetime(2020, 10, 1, 0, 0, 0, tzinfo=tokyo) # Asia/Tokyoタイムゾーンでの現在時刻を取得
print(now.isoformat())