Learn Languages 2018 というイベントで、最近のPythonについて発表してきました。

(一昨年まではLightweight LanguageでLLイベントだったのが、去年からLearn Languagesイベントになったらしい！)

せっかくなのでポイントだけでも書き起こして見ようと思います。

ここ1~2年で便利になった機能

pathlibはファイルパスに関するモジュールで3.4で導入されました。

ただ、build-inのopenやos.pathモジュールがpathlib.Pathオブジェクトを受け入れられるようになったのがPython3.6(PEP519)となっています。

3.6以降ではぜひos.pathよりもpathlibを優先して使っていきたいです。

使用例: スクリプトのディレクトリを取得

今までは、スクリプトのあるディレクトリを取得するとき

import os root_path = os.path.dirname(os.path.abspath(__file__))

こんなかんじでos.pathモジュールを駆使していました。

これを、pathlibを使うと

import pathlib root_path = pathlib.Path(__file__).resolve().parent

というように

現在のファイルのPathオブジェクトを作る resolveで相対パスを絶対パスに解決 親ディレクトリの取得

みたいに直感的にかけるようになりました。

使用例: ディレクトリ以下のファイルの探索

ディレクトリ以下のcsvファイル探索をこんなふうに簡単にかけます。

data_path = pathlib.Path( 'data' ) for filename in data_path.glob( '**/*.csv' ): print (filename)

今までは os.walk を使って書いてたのに比べるとだいぶ楽ちんです。

TypeHintっていうのは 引数や関数の返り値に型注釈をつけられる仕様です。

def greeting (name: str ) -> str : return 'Hello ' + name print (greeting.__annotations__)

このサンプルだと

引数nameはstr型であり

関数greetingはstr型を返す

ということを示しています。

これはあくまで静的解析のための構文で、 実際にビルドするときに型チェック等は行われるわけではありません。 しかし、この構文によってIDE等での解析が可能になります。

ただ、もともと型注釈の評価はコンパイル時に行われていたので 、

class C : @ classmethod def from_string (cls, source: str ) -> C: …

こんなふうにCというクラスが自分自身のクラスを返すという型注釈をつけると、 その時点ではまだクラスCはクラスCの存在を知らず、実行時にエラーが起こってしまっていました。

これを踏まえ、3.7ではアノテーションの遅延評価(PEP 563) が導入されました。 型注釈の評価が実行時に行われるようになり、このサンプルのような前方参照が可能になりました。

これでTypeHintがかなり使いやすくなったのではないかと思います。

ただ、互換性のない変更になるので利用する際には

from __future__ import annotations

の一文が必要になります。

この挙動は4.0から標準の挙動になる予定です。

クラス変数アノテーションを使って変数を定義することで、

__init__ , __repr__ , __eq__ , __ne__ , __lt__ , __le__ , __gt__ , __ge__

を自動的に生成できるDataClassが3.7で導入されました。

今までname, age, itemsの３つをもった Userクラスを定義しようとすると、こんな感じでした。

from typing import List class User : name: str age: int items: List[ int ] = None def __init__ (self, name: str , age: int = 0 ): self.name = name self.age = age self.items = [] def __repr__ (self): return f’{self.__class__.__name__}’ .... user = User( 'Taro' , 10 )

インスタンス変数が多いクラスを書くときに、 この __init__ で引数と代入部分で 何度も同じ変数名書くのめんどくさい… と思ったことないですか。

私はよく正規表現駆使して __init__ 生成してました。

またitemのようにmutableなオブジェクトは

関数の デフォルト引数に item = [] というように書くことはできません。

上記サンプルのように __init__ で初期化を書くようにしないと、 インスタンス間で オブジェクトがシェアされるような挙動になってしまうためです。 (これはPythonの落とし穴！)

3.7で導入されたDataclasses(PEP 557)のdataclassデコレータを使うと、 上記コードはこんな感じになります。

from dataclasses import dataclass, field from typing import List @ dataclass class User : name: str age: int items: List[ int ] = field(default_factory= list ) user = User( 'Taro' , 10 )

__init__ や __repr__ をかかなくてもよくなり、すっきりしました。

また、このdataclassesモジュールのfield関数を使うと デフォルト値としてlistのような mutableなvalueを設定できます！

dataclassは、あくまで 自動で特殊関数をはやしてくれる普通のクラスなので、 どんどんこの機能は使っていけたらいいなと思っています。

Other update

他にも特に面白そうな新規機能としては以下を挙げておきます。

興味がある方は見てみてください。

これからのPython: Python3.8

Python3.8はいまのところ2019年10月にリリース予定です。

組み込まれる予定の機能で一つ大きなのが 「Assignment Expression(代入演算子)の導入」があるのでこれについて少しだけ書いていきます。

今までなら正規表現のmatchしたオブジェクトを 一度変数に入れてifで評価してたのが

match = pattern.match(data) if match: result = match.group( 1 )

代入演算子を使うと、こんな感じで代入と評価を同時にすることができます。

if (match := pattern.search(data)) is not None : result = match.group( 1 )

便利ですね。

しかし、これは本当にPythonicなのか？ということで、導入までには議論を呼びました。

Pythonには、 Zen of Python (PEP 20) の

There should be one-- and preferably only one --obvious way to do it.

にもあるように「同じことを複数の書き方ができるようにしない」ということを大切にしています。

Pythonはインクリメント演算子もないくらいその部分を気にしているのに、 := はいいのか・・・という気持ちは個人的にはあります。

ただPythonも色んな人に使われるようになってきたので、 そのあたりは柔軟になっていくのかもしれないです。

(おまけ) Retirements of Guido van Rossum

ところで、最近PythonのBDFL(Benevolent Dictator For Life, 最終意思決定者)であるGuidoがBDFLの引退を発表しました。

... I would like to remove myself entirely from the decision process. I'll still be there for a while as an ordinary core dev, and I'll still be available to mentor people -- possibly more available. But I'm basically giving myself a permanent vacation from being BDFL, and you all will be on your own. ....

[python-committers] Transfer of power

Guidoはこのポジションについて 後継者を決めるつもりはなく、 あとはみんなで決めてね、というように言っています。

今後のPythonの意思決定がどうなっていくかは追っていきたいところです。