Keep on moving

あんまりまとまってないことを書きますよ

Python3.7のPEP 563: Postponed evaluation of annotations はいいもんだなっていう話

大家好, レアキャラです。 今日はPython3.7の新機能 で気になってるやつを紹介したいと思います。

TL;DR

version
Python 3.6.4, 3.7b2
mypy 0.580

Python3.7で __future__annotations ってのが追加されて、TypeHintの痒いところに手が届くようになる。

PEP 563 -- Postponed Evaluation of Annotations | Python.org

Python 3.7での変更点

ドキュメントにも書いてあるんですが、軽くコードで書くと

What’s New In Python 3.7 — Python 3.8.0a0 documentation

class C:
    @classmethod
    def from_string(cls, source: str) -> C: # クラス内で自分自身のクラス(この場合 `C`)が使えるように
        ...

    def validate_b(self, obj: B) -> bool: # クラスBが定義される前でも使えるように
        ...

class B:
    ...

class だとこんな感じで結構制約があったのが変更されるようになります。 一個例を上げときます。例えばこんな感じのコードです。

# -*- coding: utf-8 -*-

class Meter:
    def __init__(self, value: int) -> None:
        self.value = value
    def add(self, other:Meter) -> None: # Python3.6だと実行時にここがエラーになる
        self.value += other.value

a = Meter(1)
b = Meter(2)
a.add(b)
print(a.value)
$ python3.6 add.py
Traceback (most recent call last):
  File "add.py", line 5, in <module>
    class Meter:
  File "add.py", line 8, in Meter
    def add(self, other:Meter) -> None:
NameError: name 'Meter' is not defined

このコードは Python3.6では、実行時エラーになります。クラス内で自分のクラスがTypeHints として使えないからです。(1) 3.7ではこれがfrom __future__ import annotations を使うことで動かせるようになります。

# -*- coding: utf-8 -*-
# from __future__ import annotations


class Meter:
    def __init__(self, value: int) -> None:
        self.value = value
    def add(self, other:Meter) -> None:
        self.value += other.value

a = Meter(1)
b = Meter(2)
a.add(b)
print(a.value)
$ python3.7 add.py
3

futureをつけないとPython3.6と同じで実行時エラーになります.これは互換性に配慮した変更です。 ちなみにこれは Python4.0でデフォルトの挙動になります。 Python3系で future が増えるのは久しぶりです。直感的にわかりやすくかけるので個人的にはなかなかいい変更ダナーと思います。


  1. ちなみに mypy(0.580) ではこれがエラーになりません。実装系ごとの違いなのかもです