Python

Python3.6の新機能

More than 1 year has passed since last update.

はじめに

以前、Python3.5の新機能という記事を書いたが、3.6もα版が出て来たので、3.6の機能を先取りして確認してみようと思う。

開発ロードマップ(PEP-494)によると

  • 3.6.0 alpha 1: 2016-05-17 (完了)
  • 3.6.0 alpha 2: 2016-06-13 (完了)
  • 3.6.0 alpha 3: 2016-07-11 (完了)
  • 3.6.0 alpha 4: 2016-08-15 (完了)
  • 3.6.0 beta 1: 2016-09-12 (完了・新機能追加はここまで)
  • 3.6.0 beta 2: 2016-10-03
  • 3.6.0 beta 3: 2016-10-31
  • 3.6.0 beta 4: 2016-11-21
  • 3.6.0 candidate 1: 2016-12-05
  • 3.6.0 candidate 2: 2016-12-12 (必要であれば)
  • 3.6.0 final: 2016-12-16

となっていて、先はまだまだ長い。最終版が出るのは12月16日。クリスマス前だ。新機能の追加も9月のベータが出るまで続くのでそれまではドンドン変わっていくと思うが、変更がある度にこの記事をアップデートしていこうと思う。その方が一気に見るよりも楽だし(^^;

という事で、今回参照したのは3.6.0b1で、ここのドラフト版リリースドキュメントを参考にした。

試す場合にはpyenvを使って

pyenv install 3.6.0b1
pyenv local 3.6.0b1

でどうぞ。

更新履歴

2016.07.16

  • 確認に使ったバージョンを 3.6.0a2から3.6.0a3に変更
  • (それ以外、気がついた変更なし...)

2016.10.01

  • 確認に使ったバージョンを 3.6.0b1に変更
  • Betaになりこれ以上の仕様変更は無いためタイトルから"(a3版)"を削除
  • 「新たな文法の導入」に global/nonlocalの変更、PEP515, PEP526, PEP525, PEP530 を追加
  • 「セキュリティの改善」を追加
  • 「Windowsでの改善」にPSP529, PEP528他を追加
  • 「新規ビルトイン機能」を追加
  • 「新機能」に追加多数
  • 「その他の言語の変更」に記述追加
  • 「改善されたモジュール」に記述追加
  • 「廃止候補」に「廃止予定のビルドオプション」を追加

変更内容

リリースハイライト

新たな文法の導入

  • globalnonlocalはそのスコープで最初にその変数を使う前に置かないとダメになった(これまでは警告のみ)
  • PEP-498 書式化文字列リテラル
  • PEP-515 数値リテラルの中に_を入れられる様になった
  • PEP-526 変数にアノテーションする記法の導入
  • PEP-525 非同期ジェネレーターの導入
  • PEP-530 非同期内包表記の導入

PEP-498、PEP-515、PEP-526は「新機能」のところで更に詳細に触れている。

セキュリティの改善

  • Linux上で、os.urandom()はシステムのurandomエントロピープールが初期化されるまでブロックすることになった。詳細な論拠はPEP524(https://www.python.org/dev/peps/pep-0524/)参照。
  • hashlibsslモジュールがOpenSSL 1.1.0をサポート
  • sslのデフォルト設定と機能セットがよりセキュアなものに
  • hashlibモジュールが新たなハッシュ関数(BLAKE2、SHA-3、SHAKE)と鍵導出関数scrypt()をサポート

Windowsでの改善

  • PEP529(https://www.python.org/dev/peps/pep-0529/) WindowsのファイルシステムエンコーディングをUTF-8に変更
  • PEP528(https://www.python.org/dev/peps/pep-0528/) WindowsのコンソールエンコーディングをUTF-8に変更
  • これまで py.exeをコマンドラインで使う時には、バージョン指定が無いとPython2を優先させていたのだが、今後はPython3になる。
  • python.exepythonw.exeがロングパス対応になり、ファイルパスの260文字制限がなくなった
  • ._pthファイルにモジュールのサーチパスを書くことができるようになった
  • PYTHONHOMEが環境変数で設定されない場合、Python36.zipの位置でそれを推測するようになった

新規ビルトイン機能

新機能

PEP-515 数値リテラルの中に_を入れられる様に

10000000000000001_000_000_000_000_000と書いたり出来るようになった。意味的な変更はまったくなく単純に読みやすさのためだけの_だ。

PEP-523 フレーム評価APIの追加

フレームオブジェクトの評価はこれまでカスタマイズできなかったが、それを出来るようにするためのCレベルのAPIを追加した。これによりJITやDebuggerの実装が楽になるらしい。

PEP-519 ファイルシステムパスプロトコルの追加

従来、ファイルシステムのパスはstr型かbytes型で、Python標準のものも含めて、パスを扱うライブラリはその想定で作られていた。そのため、pathlibのような、パスを表現するライブラリを既存のライブラリと組み合わせて使うのが困難だった。

これを解消するためにos.PathLikeというインターフェース(仮想基底クラス)を定義した。パスを表すオブジェクトは、これを継承し、パスの文字列表現を返す__fspath__()というメソッドを定義することで、他のライブラリがこれをパスだと認識して動作してくれることになった。

PEP-498 書式化文字列リテラル

文字列の前にfをつけると書式化文字列になる。str.formatの書式にも似ているが、{}で囲まれた部分が変数として置換される。例えばこんな感じ。

>>> name = "モンティ・パイソン"
>>> f"私の名前は{name}です"
'私の名前はモンティ・パイソンです'

これが、str.formatを使うと

>>> "私の名前は{name}です".format(name=name)

あるいは

>>> "私の名前は{}です".format(name)

としなければならなかったので、少し記述が楽になりそう。

PEP-526 変数にアノテーションする記法の導入

Pythonは明示的な型指定のない動的型付けの言語だが、そのため静的解析などをやりにくいという問題があった。それを解消するために、PEP-484(https://www.python.org/dev/peps/pep-0484/)では関数の引数に型情報を付記するという方法が提案され、Python3.5で導入された。

これをさらに拡張し、変数(クラス変数、インスタンス変数を含む)にも型情報を付けられるようにしたというのが今回の変更。従来もコメントを使えば同じように出来たが、文法の変更で対応したというのが違いになる。例えばこんな感じ。

from typing import List, Dict

primes: List[int] = [] # 整数のリスト型の例で、それを空リストで初期化

captain: str  # 文字列型。初期化しない例

class Starship:
    stats: Dict[str, int] = {} # クラス変数の例。Keyとして文字列、値として整数を取る辞書型

なお、この型アノテーションで何か動作が変わるわけではなく、違った型の値を代入してもエラーにはならない。あくまでも、型情報の「ヒント」であり、この情報を元に静的解析をしたりという用途に向けたものになる。

PEP-529 WindowsのファイルシステムエンコーディングをUTF-8に変更

従来はWindowsの上でBytesを使ってパスを受け渡しするとデータ欠損とかが出る場合があった。Python 3.6からはBytesを使っていたとしてもシステムのデフォルトエンコーディングでエンコードされて返ってくることになった。なお、デフォルトエンコーディングは今のところUTF-8だが、ベータ期間のフィードバックに寄って最終的に変わるかもしれないとのこと。

PEP-487 クラス生成の簡単カスタマイズ

クラスが継承された場合に、__init_subclass__(cls)というクラスメソッドが呼ばれる事になった。この中でカスタマイズするコードを書くことが出来る。また、ディスクリプタオブジェクトの初期化時に__set_name__というメソッドを呼ぶことになった。

PEP-528 WindowsのコンソールエンコーディングをUTF-8に変更

全てのUnicode文字を受け付けるようになり、正しくstrオブジェクトに読み込むことができるようになった。そして、sys.stdinsys.stdoutsys.stderrのデフォルトエンコーディングはUTF-8となった。

PYTHONMALLOC環境変数

PYTHONMALLOCという環境変数にある値を入れることによってPythonのメモリ割り当ての動作を変えるたりデバッグの為のフックを仕掛けることができる。

PYTHONMALLOC=debug

この設定にすると、以下のデバッグ機能が有効になる
- 新たに割り当てられたメモリ領域は0xCBで埋められる
- 開放された領域は0xDBで埋められる
- メモリ割り当てAPIの不正な利用が検出される。例えばPyMem_Malloc()で取った領域をPyObject_Free()で開放したりした場合。
- 割り当て領域の前に書き込んだり(バッファアンダーフロー)、後に書き込んだり(バッファオーバーフロー)した場合を検出。
- メモリ割り当て関数が呼ばれた時にGIL(Global Interpreter Lock)が確保されているかどうかの確認。

PYTHONMALLOC=malloc

この設定にすると、Cライブラリのmallocを強制的に使うようになる。これによって、Valgrindなどの外部ツールが使えるようになる。

PYTHONMALLOC=malloc_debug

上記2つの組み合わせ

DTraceとシステムタップのサポート

--with-dtrace付きでコンパイルすると以下のイベントを検知するマーカーが有効になる。

  • 関数呼び出し、関数復帰
  • ガベージコレクションの開始と終了
  • 実行されているコードの行番号

この機能によってPythonをデバッグ用に再コンパイルすることなくデバッグやプロファイリングができる。

PEP-520 クラス属性の定義順序保持

クラスの属性はソースコードに記述された順序で、クラスの__dict__属性に保持される。

PEP-468 キーワード引数順序の保持

関数のキーワード引数はその順序を保持された形で**kwargsに渡される。

PEP-509 辞書型オブジェクトに内部バージョン導入

辞書型オブジェクトに変更が加わったときのみ更新されるバージョンを導入した。それにより、各種の最適化(性能向上)を行うことが可能になる。

その他の言語の変更

dict()はPyPyで由来のコンパクトな表現を使うようになり、メモリ使用量はPythono3.5と比べて20-25%少ない。そして、PEP-468のキーワード引数の順序保持はこれを使って実装されている。一方、「順序が保存される」という性質は実装依存のもので、将来的に変わる可能性があるので、それを前提にした実装はするべきではない。

  • トレースバックの長い繰り返しは省略して表記されるようになった

  • importがモジュールを見つけられなかった場合はModuleNotFoundErrorを返すようになった

新規のモジュール

(今のところなし)

改善されたモジュール

asyncio

asyncioは「provisional(暫定的な)」と定義されているので、全ての変更は3.5.xにもバックポートされる。

3.5.0からの主な変更点は以下の通り。

  • ensure_future()関数とそれを使う関数は、すべての種類のawaitable オブジェクトを引数に指定できるようになった。
  • run_coroutine_threadsafe()関数が新たに導入されて、他のスレッドからイベントループにコルーチンを投入できるようになった。
  • Transport.is_closing()メソッドが追加され、トランスポートが閉じられている(あるいは閉じている途中)かが確認できるようになった。
  • loop.create_server() メソッドはホストのリストを引数に取れるようになった。
  • loop.create_future() メソッドが追加され、Futureオブジェクトを作成できるようになった。これによって、代替のイベントループ実装が可能になり、より速いasyncio.Futureを提供できるようになる。
  • loop.get_exception_handler()メソッドが追加され、現在の例外ハンドラーを取得できるようになった。
  • timeout()コンテキストマネージャによってタイムアウト処理のコードが簡単にかけるようになった。
  • StreamReader.readuntil()メソッドが追加され、セパレータバイトで句切られたストリームを読めるようになった。
  • loop.getaddrinfo()メソッドが改善され、アドレスが既に名前解決していれば、OS側のgetaddrinfoを呼ばなくても良くなった。

asyncioはあまり使ったことが無かったが、この中でrun_coroutine_threadsafe()を使ってみた。

asyncio_test.py
import asyncio

@asyncio.coroutine
def wakeup(name, repeat):
    for i in range(repeat):
        yield from asyncio.sleep(1)
        print("{}/{}: 起きた!".format(name, i))
    return repeat

def target(name, repeat, loop):
    future = asyncio.run_coroutine_threadsafe(wakeup(name, repeat), loop)
    return future.result()

loop = asyncio.get_event_loop()

futures = [
    loop.run_in_executor(None, target, "A", 1, loop),
    loop.run_in_executor(None, target, "B", 2, loop),
    loop.run_in_executor(None, target, "C", 3, loop),
]

loop.run_until_complete(asyncio.wait(futures))
loop.close()

ちょっとややこしいが、wakeupというのが実行するコルーチン。1秒間寝た後に、nameで与えられた名前を名乗って「起きた!」という行をPrintするだけ。それをもう一つの引数repeatの回数だけ繰り返す。そのコルーチンを実行するのがtargetという関数で、その中でrun_coroutine_threadsafeを呼んでいる。それを別々のスレッドで実行するために、run_in_executorで実行している。

実行結果はこんなふうになるはず。

$ python asyncio_test.py
A/0: 起きた!
B/0: 起きた!
C/0: 起きた!
B/1: 起きた!
C/1: 起きた!
C/2: 起きた!

なお、上にも書いたようにこのasyncioの変更は3.5シリーズにもバックポートされ、実際もう3.5.1で実装されているようだ。実際、上の例も問題なく動く。

contextlib

コンテキストマネージャの仮想ベースクラスとしてcontextlib.AbstractContextManagerが追加され、__enter__()__exit__()のデフォルト実装を提供するようになった。また、typingモジュールにも同様のクラスが追加された。

venv

venvは新たに--promptパラメータを受け取るようになった。これにより仮想環境時にコマンドラインプロンプトのprefixを変えられる。

datetime

datetime.strftime()メソッドとdate.strftime()メソッドがISO 8601の%G%u%Vをサポートするようになった。%Gと%Vは週単位表記での年と週番号(01-53)を表し、%uは週の何番目かを表す(月曜日が1で日曜日が7)。

distutils.command.sdist

default_format 属性は distutils.command.sdist.sdistから除去され、デフォルトの属性は['gztar']になった。想定外だが、この属性に依存したコードは全て修正されなければならない。

email

policy引数の追加された新たなメールAPIが「provisional(暫定)」ではなくなり、ドキュメントもそれを中心に書き直された。従来のドキュメントはレガシーAPIとして残されている。

email.mimeクラスやDecodedGeneratorはpolicy引数を受け取るようになった。

policyオブジェクトにmessage_factory属性が追加され、パーサが新たなメッセージを生成する時にどのメッセージクラスを使うのか指定できるようになった。

encodings

Windows環境上で、CP_OEMCPを使う'oem'が追加された。また、CP_ACPを使うmbcsansiというエイリアスが加えられた。

faulthandler

Windows環境でWindows例外を発生させるハンドラーをインストールするようになった。

hashlib

hashlibはOpenSSL 1.1.0をサポートした。推奨バージョンは1.0.2以上だが、 0.9.8zc、0.9.8zh、1.0.1t と LibreSSL 2.3、2.4でもテストされている。

BLAKE2ハッシュ関数のサポートが追加され、blake2b()、blake2s()が利用できるようになった。

SHA-3ハッシュ関数 sha3_224()、sha3_256()、sha3_384()、sha3_512() と SHAKEハッシュ関数 shake_128()、shake_256()が追加された。

パスワードベースの鍵導出関数 scrypt()がOpenSSL 1.1.0以上の場合に利用可能になった。

http.client

HTTPConnection.request() と endheaders() がリクエストボディで chunked エンコーディングをサポートした。

idlelibとIDLE

idlelibパッケージが今風にリファクタリングされIDLEの見た目や操作性・視認性が向上した。変更にはファイル名の変更も含まれ、3.5までのコードはそのままだと動かない。

importlib

importlib.util.LazyLoaderがラッパーローダーの上でcreate_module()を呼ぶようになったのでimportlib.machinery.BuiltinImporterimportlib.machinery.ExtensionFileLoaderimportlib.util.LazyLoaderと一緒に使えないという制約がなくなった。

importlib.util.cache_from_source()importlib.util.source_from_cache()importlib.util.spec_from_file_location()はパス風オブジェクトを受け取れるようになった。

json

json.load()json.loads()はバイナリを入力として受け取れるようになった。エンコードされたJSONはUTF-8、UTF-16、UTF-32のいずれかでエンコードされていなければならない。

os

close()メソッドが提供され、scandir()のイタレーターを明示的に閉じれるようになった。そして、scandir()のイタレーターがコンテキストマネージャプロトコルをサポートするようになった。scandir()のイタレーターが使い切られていない、あるいは明示的に閉じられなかった場合には、ResourceWarning例外がデストラクタの中で発生する。これは、メモリリーク対策だと思うが、withステートメントと一緒に使えば自動で呼んでくれる。

Linuxのgetrandom()システムコールは新たなos.getrandm()関数を介して利用可能になった。

pickle

__new__をキーワード引数と共に呼ばなければならなかったオブジェクトの塩漬け(ピクル化)ができるようになった。これはpickle version 4以上であればこれまでもできていたが、今回の変更でversion 3以下でもできるようになったとのこと。

re

正規表現にかかる修飾子が追加された。例えば'(?i:p)ython''python''Python'にはマッチするけど'PYTHON'にはマッチしない。同様に'(?i)g(?-i:v)r''GvR''gvr'にはマッチするけど'GVR'にはマッチしない。

readline

set_auto_history()が追加され、履歴への自動追加有無が制御できるようになった。

rlcompleter

rlcompleterはreadlineのcompletion(補完機能)。ひとつ目の変更は、アンダースコア('_')で始まるモジュール変数・メソッド・属性を補完のリストから外したこと。と言っても分かりにくいので、例を。これまでは、Pythonを対話モードで立ち上げて、"int."と打ち込んだ後にTABキーを2回押すとこうなった

$ python
Python 3.5.2 (default, Jul  7 2016, 23:37:57)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> int.<TAB><TAB>
int.__abs__(            int.__dir__(            int.__hash__(           int.__mul__(            int.__reduce__(         int.__rtruediv__(       int.__xor__(
int.__add__(            int.__divmod__(         int.__index__(          int.__name__            int.__reduce_ex__(      int.__rxor__(           int.bit_length(
int.__and__(            int.__doc__             int.__init__(           int.__ne__(             int.__repr__(           int.__setattr__(        int.conjugate(
int.__base__(           int.__eq__(             int.__instancecheck__(  int.__neg__(            int.__rfloordiv__(      int.__sizeof__(         int.denominator
int.__bases__           int.__flags__           int.__int__(            int.__new__(            int.__rlshift__(        int.__str__(            int.from_bytes(
int.__basicsize__       int.__float__(          int.__invert__(         int.__or__(             int.__rmod__(           int.__sub__(            int.imag
int.__bool__(           int.__floor__(          int.__itemsize__        int.__pos__(            int.__rmul__(           int.__subclasscheck__(  int.mro(
int.__call__(           int.__floordiv__(       int.__le__(             int.__pow__(            int.__ror__(            int.__subclasses__(     int.numerator
int.__ceil__(           int.__format__(         int.__lshift__(         int.__prepare__(        int.__round__(          int.__subclasshook__(   int.real
int.__class__(          int.__ge__(             int.__lt__(             int.__qualname__        int.__rpow__(           int.__text_signature__  int.to_bytes(
int.__delattr__(        int.__getattribute__(   int.__mod__(            int.__radd__(           int.__rrshift__(        int.__truediv__(
int.__dict__            int.__getnewargs__(     int.__module__          int.__rand__(           int.__rshift__(         int.__trunc__(
int.__dictoffset__      int.__gt__(             int.__mro__             int.__rdivmod__(        int.__rsub__(           int.__weakrefoffset__
>>> int.

これが、3.6からはこうなる。

ython 3.6.0a3 (default, Jul 16 2016, 00:35:58)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> int.
int.bit_length(  int.conjugate(   int.denominator  int.from_bytes(  int.imag         int.mro(         int.numerator    int.real         int.to_bytes(

_から始まる名前が補完候補から外されている。それらを表示したければ、_を打ち込んでから<TAB><TAB>

>>> int.__
int.__abs__(             int.__dict__             int.__getattribute__(    int.__lt__(              int.__prepare__(         int.__ror__(             int.__subclasscheck__(
int.__abstractmethods__  int.__dictoffset__       int.__getnewargs__(      int.__mod__(             int.__qualname__         int.__round__(           int.__subclasses__(
int.__add__(             int.__dir__(             int.__gt__(              int.__module__           int.__radd__(            int.__rpow__(            int.__subclasshook__(
int.__and__(             int.__divmod__(          int.__hash__(            int.__mro__              int.__rand__(            int.__rrshift__(         int.__text_signature__
int.__base__(            int.__doc__              int.__index__(           int.__mul__(             int.__rdivmod__(         int.__rshift__(          int.__truediv__(
int.__bases__            int.__eq__(              int.__init__(            int.__name__             int.__reduce__(          int.__rsub__(            int.__trunc__(
int.__basicsize__        int.__flags__            int.__instancecheck__(   int.__ne__(              int.__reduce_ex__(       int.__rtruediv__(        int.__weakrefoffset__
int.__bool__(            int.__float__(           int.__int__(             int.__neg__(             int.__repr__(            int.__rxor__(            int.__xor__(
int.__call__(            int.__floor__(           int.__invert__(          int.__new__(             int.__rfloordiv__(       int.__setattr__(
int.__ceil__(            int.__floordiv__(        int.__itemsize__         int.__or__(              int.__rlshift__(         int.__sizeof__(
int.__class__(           int.__format__(          int.__le__(              int.__pos__(             int.__rmod__(            int.__str__(
int.__delattr__(         int.__ge__(              int.__lshift__(          int.__pow__(             int.__rmul__(            int.__sub__(
>>> int.__

それから、幾つかのキーワードを補完した後にスペース(' ')かコロン(':')が付くようになった。例えば、wiと打ち込んで<TAB>を押すとwithとスペース付きで補完されるし、trと打ち込んだから<TAB>するとtry:となる。地味に便利。

あと、これまで表示されなかった@propertyで作られた属性とかの補完ができるようになったともあるが、これはよく判らなかった。

site

sys.pathにパスを追加する際に重複チェックしているが、それがうまく動かない場合の修正っぽい。

sqlite3

sqlite3.Cursor.lastrowidは最後に変更した列の列IDを返すが、これまでINSERTを実行した時のみ有効だったが、REPLACEを実行した時にも取れるようになった。

socket

ioctl()関数でSIO_LOOPBACK_FAST_PATHが使えるようになり、Windows環境(Win8以上)でTCP loopbackのパフォーマンスを向上させることができるようになった。

getsockopt()の定数SO_DOMAINSO_PROTOCOLSO_PEERSECand SO_PASSSECがサポートされた。

アドレスファミリー AF_ALGがサポートされ、LinuxカーネルのCrypto APIを利用できることになった。

socketserver

socketserverモジュールに基づくサーバー(例えばhttp.server, xmlrpc.serverwsgiref.simple_serverで定義されている)がコンテキストマネージャプロトコルをサポートするようになった。

StreamRequestHandlerクラスのwfile属性がio.BufferedIOBasewritableインターフェイスを実装した。特に、write()はデータを全て送ることが保証されるようになった。

ssl

sslはOpenSSL 1.1.0をサポートした。推奨バージョンは1.0.2以上だが、 0.9.8zc、0.9.8zh、1.0.1t と LibreSSL 2.3、2.4でもテストされている。

3DES はデフォルト暗号スイートから外された。また、ChaCha20 Poly1305 暗号スイートは正しい場所に置かれることになった。

SSLContextのデフォルトコンフィギュレーションはより良いオプションと暗号を使うようになった。

SSLセッションはクライアント側から相手側にSSLSessionを使ってコピーできるようになった。TLSセッションの再開が速度改善された。

全ての定数やフラグはIntEnumからIntFlagsへ変更された。

SSLContext作成時に引き渡すパラメータとして、Server-sideのみ、Client-sideのみのプロトコルが追加された。

サブジェクトの別名拡張領域にGeneralリソースIDを入れてもシステムエラーにならなくなった。

subprocess

subprocess.Popenで作られた子プロセスが実行中にのPopenオブジェクトを消去しようとすると、ResourceWarning例外が起こるようになった。

telnetlib

Telnetがコンテクストマネージャプロトコルを実装。

tkinter

tkinter.Variableクラスにtrace_add() trace_remove() trace_info()メソッドを追加し、Tclの古いAPIを使っていた実装を置き換えた。

traceback

トレースバックの長い繰り返しは省略して表記されるようになった。例えば自己ループする関数を実行するとこのようになる。

>>> def f(): f()
...
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in f
  File "<stdin>", line 1, in f
  File "<stdin>", line 1, in f
  [Previous line repeated 995 more times]
RecursionError: maximum recursion depth exceeded

typing

contextlib.AbstractContextManagerの為のtyping.ContextManagerクラスが追加された(前述のとおり)。

unicodedata

内部のデータベースが Unicode 9.0.0にアップグレードされた。

unittest.mock

MockクラスにMock.assert_called()Mock.assert_called_once()が追加され、mockオブジェクトが呼ばれたかどうかを確認できるようなった。

urllib.request

HTTPリクエストがファイルか繰り返し可能なボディ(ただしバイトオブジェクトを除く)を持ちながら、Content-lengthヘッダが指定されなかった場合、これまではエラーになっていたが、Chunked転送エンコーディングを行って送出するようになった。

urllib.robotparser

RobotFileParserCrawl-delay拡張とRequest-rate拡張をサポートするようになった。これらはrobots.txtでクローリング間隔の調整に使うものだ。
- Crawl-delay: クローリングを始める前に待たなければならない時間(秒)
- Request-rate: 一定時間内に要求できるページ数

warnings

warnings.warn_explicit()関数にsourceパラメータが追加され、ResourceWarningを発生させたオブジェクトがわかるようになった。source属性はwarnings.WarningMessageにも追加されている。

そして、ResourceWarningがあがる時にはtracemallocが使われ、消去対象のオブジェクトのメモリのトレースバックが取れるようになった。こんな例が挙げてある。

example.py
import warnings

def func():
    return open(__file__)

f = func()
f = None

func関数の中でファイルをオープンし、その直後にそのファイルオブジェクトへの参照をNULLで上書きすることで切ってしまっている。これを実行するとこのようになる。

$ python -Wd -X tracemalloc=5 example.py
example.py:7: ResourceWarning: unclosed file <_io.TextIOWrapper name='example.py' mode='r' encoding='UTF-8'>
  f = None
Object allocated at (most recent call first):
  File "example.py", lineno 4
    return open(__file__)
  File "example.py", lineno 6
    f = func()

winreg

Windows環境のレジストリを操作するwinregモジュールで64ビット整数のREG_QWORD型がサポートされた。

winsound

Beep、MessageBeep、PlaySoundでキーワード引数を渡せるようになった。

xmlrpc.client

Apache XML-RPC実装で新たに導入された数値型とNoneの非整列化(unmarshalling)をサポート

zipfile

ZipInfo.from_file()というクラスメソッドが追加され、ZipInfoオブジェクトをファイル名を指定して作れるようになり、ZipInfo.is_dir()メソッドでそれがディレクトリであるかどうかを確認できるようになった。そして、ZipFile.open()はZIPファイルからデータを取り出すだけでなく、書き込みを行うためにも使えるようになった。

zlib

compress()関数がlevelをキーワード引数として指定できるようになった。

fileinput

hook_encoded()でファイルオープン時に使うエンコーディングだけでなく、エラーハンドラーも指定できるようになった。詳細はopen()errorsパラメータを参照のこと。

最適化

  • ASCIIデコーダは、エラーハンドラーがsurrogateescape ignore replaceの時に最大60倍速くなった。
  • ASCIIとLatin1エンコーダは、エラーハンドラーがsurrogateescapeの時に最大3倍速くなった。
  • UTF-8エンコーダは、エラーハンドラーがignore replace surrogateescape surrogatepassの時に最大75倍速くなった。
  • UTF-8デコーダは、エラーハンドラーがignore replace surrogateescapeの時に最大15倍速くなった。
  • bytes % args が最大2倍速くなった。
  • bytearray % args が2.5倍から5倍速くなった。
  • bytes.fromhex()bytearray.fromhex()が2倍から3.5倍速くなった。
  • bytes.replace(b'', b'.')bytearray.replace(b'', b'.')が最大80%速くなった。
  • PyMem_Malloc()ドメインのメモリ割り当てにCライブラリのmallocに代わってpymallocを使うようになった。pymallocは512バイト以下の短命のオブジェクト向けに最適化されていて、それよりも大きなブロックの確保にはmallocを使う。
  • pickle.load()pickle.loads()は大量の小さなオブジェクトをロードする際に最大10%速くなった。
  • キーワード引数は位置引数を引き渡すよりもオーバーヘッドがあるが、Argument Clinic (CPythonのCコードの為のプリプロセッサ)を使った機能により、そのオーバーヘッドは大幅に削減された。
  • globモジュール中のglob()とiglob()が最適化され、3から6倍速くなった。
  • os.scandir()を使ったpathlibのglobbingを最適化し、1.5から4倍速くなった

ビルドとC APIの変更

  • PythonをビルドするのにC99をサポートしたツールチェーンが必要になった。
  • CpythonをAndroid NDKでクロスコンパイルし、Android API レベル21以上で正常に動かすことができるようになった。Androidはまだ正式にサポートしているプラットフォームではないが、Androidエミュレータ上での標準テストスイートの実行結果は良好(16の失敗のみ)である。
  • コンフィギュレーションフラグに--with-optimizationが追加された。これを指定するとLTO(Link Time Optimization:リンク時最適化)とPGO(Profile-Guided Optimization:プロファイルに基づく最適化)ビルドサポートが有効になる。
  • Py_FinalizeEx() APIが導入され、インタープリターの終了処理が成功したかどうか(終了処理中にエラーがあったかどうか)が判るようになった。
  • PyArg_ParseTupleAndKeywords()が位置専用引数のサポートを行うようになった。
  • PyTrackback_Printは長い繰り返しを省略して表示するようになった。

廃止候補

廃止予定のビルドオプション

--with-system-ffiオプションはOSX以外のプラットフォームではデフォルトで有効になった。--without-system-ffiで無効にすることはできるが、このフラグの使用はPython 3.7以降できなくなる。OSXはこの変更の影響を受けない。

新たなキーワード

asyncawaitを変数名、クラス名、関数名、モジュール名として使うことは非推奨となった。PEP-492によってPython3.5で導入されたasyncawaitはPython 3.7で正式なキーワードになる予定。

廃止予定のPythonモジュール、関数、メソッド

  • importlib.machinery.SourceFileLoader.load_module() importlib.machinery.SourcelessFileLoader.load_module()は廃止予定となった。
  • tkinter.tixモジュールは廃止予定となった。代りにtkinter.ttkを使うべし。

廃止予定のC APIの関数および型

  • (今のところなし)

廃止予定の機能

  • pyvenvスクリプトは廃止予定。代りにpython3 -m venvの仕様が推奨される
  • 相対インポートを行うときに__spec__あるいは__package__が定義されていないとImportWarning例外を起きるようになった。
  • dbm.dumbモジュールは常にアップデート可の状態でオープンされ、存在しなければ作成されていた。他のdbmの実装と合わせるためにこの動作は廃止予定とされ、Python 3.8で利用不可となる。
  • 幾つかのモジュールでパスとして使われていたバイト風オブジェクトの利用は廃止予定となった。
  • disutilsのextra_pathは廃止予定となった。
  • バックスラッシュと文字の組み合わせで正しいエスケープシーケンスになっていない場合にはDeprecationWarningを起こすことになった。将来的には文法エラーになるが暫くはこのまま。
  • インラインフラグ(?letters)は正規表現の先頭でのみ使えることとなった。
  • SSL関連のcertfilekeyfilecheck_hostnameのような引数は、ftplib、http.client、imaplib、poplib、smtplibなどで使われているがそれは廃止予定となった。代りにcontextを使うことが期待される。
  • sslモジュール内の幾つかのプロトコルや関数が廃止予定となった。将来のOpenSSLでなくなるものや、他の代替手段が提供されたものなど。

廃止予定のPythonの動作

  • ジェネレータ内でStopIterationをあげるとDeprecationWarningがあがる様になった。3.7からはこれがRuntimeErrorとなる。

削除済み

APIと機能の削除

  • inspect.getmoduleinfo()は削除された。あるパスのモジュール名を知るにはinspect.getmodulename()を使う。
  • traceback.Ignoreクラスとtraceback.usage traceback.modname traceback.fullmodname traceback.find_lines_from_code traceback.find_lines traceback.find_strings traceback.find_executable_linesメソッドはtracebackモジュールから消去された。これらは3.2から廃止候補でプライベートメソッドで同様の機能は提供されている。
  • tk_menuBar() tk_bindForTraversal()ダミーメソッドはtkinterウィジェットクラスから消去された。
  • zipfile.ZipFileクラスのopen()メソッドは、'U'モードのサポートを廃止。代替としてio.TextIOWrapperを圧縮ファイルの読み込みに使うべし。

Python 3.6へのポーティングに際して

ここでは、あなたのコードをPython3.6へポーティングする際に気をつけるべきことを列挙する。

Pythonコマンドの動作

  • COUNT_ALLOCS SHOW_ALLOC_COUNT SHOW_TRACK_COUNTマクロを定義してビルドしたPythonの出力はデフォルトでオフになった。これをオンにするには -X showalloccountを引数に追加してPythonを起動すると良い。また出力先はstdoutからstderrになった。

Python APIの変更

  • (ちょっと細かいので割愛)

C APIの変更

  • PyMem_Malloc()メモリ割り当てはシステムのmalocではなく、pymallocを使うようになったので、GILを取らずにPyMem_Malloc()を呼ぶアプリはクラッシュするようになる。PYTHONMALLOC環境変数をdebugにすればメモリ割り当てが確認できる。
  • Py_Exit()(とメインのインタープリター)は終了時のメモリ解放に失敗すると120を返すようになった。

最後に

ざっと変更点を見るつもりだったが、見ているうちに色々と興味深い点が出てきて、思ったよりも時間がかかってしまった。今現在、b1でこれ以上の変更は定義上はないので、タイトルから"(α3版)"を消した。3.6のfinalが12月中旬の予定なのでもしかするとまた変更が入るかもしれないが、適宜アップデートしていこう。