SE(たぶん)の雑感記

一応SEやっている筆者の思ったことを書き連ねます。会計学もやってたので、両方を生かした記事を書きたいと考えています。 でもテーマが定まってない感がすごい。

VSCodeのPythonで、型推定が強化された模様(2018/12/2現在)

2018/12/3 追記: Python language server extensionというものが必要そうです。 「Visual Studio Intellicode」のインストールが必要かもしれません。後ほど調べて追記します。

当ブログでは、Visual Studio Codeの紹介をよくやっています*1

Pythonの紹介もその一つで、以下のような記事を書いています。

hiroronn.hatenablog.jp

hiroronn.hatenablog.jp

今回は、Visual Studio Code拡張機能Pythonがアップデートされ、カーソルを合わせた時のヒント表示が強化されたようです*2

バージョン

ツール バージョン
Visual Studio Code 1.29.1
Python(本体) 3.7.0
Python拡張機能 2018.11.0 (29 Nov 2018)

使うソース

以前当ブログで使ったものです。これのchapter-16のものを例として使います。

github.com

当時の記事はこちら。

hiroronn.hatenablog.jp

どう変わったか

以前がどうなっていたか、お見せするするものがないのですが…

例えば、dict型の変数を定義して、その変数にカーソルを合わせると、それはdictと表示されます。

しかし、そのdictに対して、要素を追加すると、なんとその型を表示してくれるようになっています。

言葉だと分かりづらいので、例えば

# dict宣言
data = {}

print(data)

だと、

dataはdictである

という表示ですが、

# dict宣言
data = {}
# 要素を追加
data["a"] = 1

print(data)

とすると、

dataはキーがstr、値がintdictである

という表示に変わります。つまり、ソースの記述によって、変数の型を判断して表示してくれるようになりました。

具体例は以下の通りです。

f:id:hiroronn:20181202172440p:plainf:id:hiroronn:20181202172626p:plain
型指定の有無による差異

以前、少なくとも2018/11/29までは、このような表示はなされていませんでした。

変わった(気がする)点を書いてみます。

戻り値の型表示

これも、以前はされていませんでした。

アノテーションで明示しなくても、型が明らかな場合は表示してくれます。

また、アノテーションがついている場合は、それを表示するようになりました。

  • expression.py
from __future__ import annotations
from abc import ABCMeta, abstractmethod
from .exchanger import CurrencyExchanger

class Expression(metaclass=ABCMeta):
    """式(演算)を表します。"""

    @abstractmethod
    def plus(self, addend: Expression) -> Expression:
        """加算"""
        pass

    @abstractmethod
    def reduce(self, bank: CurrencyExchanger, currency: str) -> Expression:
        """式を単純な形に変形する"""
        pass

    def times(self, multiplier: int) -> Expression:
        """指定倍"""
        pass
  • Money.py(継承先。一部抜粋)
    def plus(self, addend: Expression) -> Expression:
        """加算"""
        from .total import Total
        return Total(self, addend)

という状態だと、plusの戻り値はExpressionアノテーションの型)とTotal(定義で返している型)の二つが表示されます。

一方、アノテーションを消すと、戻り値はTotalのみとなります。

f:id:hiroronn:20181202181331p:plainf:id:hiroronn:20181202181453p:plain
アノテーション有無による違い

何が言いたいかというと、実際の定義に基づいた表示定義に基づいた表示の二つを表示するようになっている、ということです。

上記画像を見てもらえばわかる通り、メソッドの引数についても同様です。

継承先や同じ型のメソッドも一緒に表示

継承(インターフェイスのような使い方)元のオブジェクトを受け取るメソッドを定義した場合、それが実装されているクラスの定義も表示されます。

  • money.py
    def reduce(self, bank: CurrencyExchanger, currency: str) -> Expression:
        rate = bank.rate(self.currency, currency)
        return Money(self.amount / rate, currency)

という定義で、bank.rateにカーソルを合わせると、本来ならCurrencyExchangerクラスのヘルプのみ表示されればよいですが、それを実装しているBankクラスのヘルプも表示されます。

f:id:hiroronn:20181202183108p:plain
継承先のヘルプ表示

ただし、出たり出なかったりします。動きがよくわかりません。(特にコメント)

listの中身がわかりやすくなった

これは、ちょうどよいサンプルが無いので、自作ソースで書きます。

たとえば、以下のようなクラスがあるとします。

class Point:
    def __init__(self, x: int, y: int):
        self._x = x
        self._y = y

そして、そのインスタンスlistに格納します。

points = []
points.append(Point(0, 0))
points.append(Point(1, 0))
points.append(Point(3, 2))

すると、points変数は、list[Point]型である、とVSCodeが認識します。つまり、型指定Listと判断してくれます。

f:id:hiroronn:20181202185011p:plain
型付リスト

次に、このリストをforループしてみます。するとなんと、中身の型推論を行ってくれます!

f:id:hiroronn:20181202185309p:plainf:id:hiroronn:20181202185312p:plain
型推論

私が記憶する限り、先日まではlistをループさせても型推論を行ってくれず、使いづらいなぁ…と愚痴をこぼしながら仕事をしていたので、この部分が強化されたのは間違いないと認識しています*3

これのよいところは、内包表記が書きやすくなる、という点にも表れます。

ps = [p for p in points if p.x >= 1]

という書き方をすると、以前は

pがなんなのかわからんよ

と言っているのか、ヘルプなんて表示されませんでした。

型推論が働くことにより、

pはPointだよ

と分かるようになり、リスト内包表記をしている場合でもIntelliSenseの恩恵にあずかれるようになりました。

個人的には大変助かる機能です。パフォーマンスチューニングのためにリスト内包を書くのが、本当に楽になります…

おわりに

本日、偶然気づいた機能ですので、まだ調べ切っていない部分はあります。

しかし、11/29までは間違いなく無かった機能が追加されたようです。

Python動的言語だというのは理解していますが、リスト内の型が明らかな場合ぐらいは判定してくれ、と思っていた(実際この一点でPythonのコーディングに嫌気がさしていたところ)ので、この改定は本当に助かります。

明日からはちょっとストレスが減りそうです。ほんとうに嬉しい。

*1:VSCode関連記事のPVが多い

*2:jediのバージョンアップを当てたから?強化された旨の記事が見つからなかった

*3:typingのListを使っても、何も出してくれなかった