Python3.7の新機能 Data Classes


先日Pythonの新機能としてData Classesが採用されました :tada:

<blockquote class="twitter-tweet" data-lang="ja"><p lang="en" dir="ltr">PEP 557 -- Data Classes accepted: <a href="https://t.co/6LDFXMBcLj">https://t.co/6LDFXMBcLj</a></p>&mdash; Python Software (@ThePSF) <a href="https://twitter.com/ThePSF/status/938377499154796544?ref_src=twsrc%5Etfw">2017年12月6日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## Data Classesとは？
[PEP 557](https://www.python.org/dev/peps/pep-0557/)のなかで、以下のように述べられている。

> Data Classes can be thought of as "mutable namedtuples with defaults". Because Data Classes use normal class definition syntax, you are free to use inheritance, metaclasses, docstrings, user-defined methods, class factories, and other Python class features.


一言で言えば、「デフォルト値があるミュータブルなnamedtuple」。

```py
@dataclass
class InventoryItem:
    '''Class for keeping track of an item in inventory.'''
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand
```
のように宣言すると、

```py
def __init__(self, name: str, unit_price: float, quantity_on_hand: int = 0) -> None:
    self.name = name
    self.unit_price = unit_price
    self.quantity_on_hand = quantity_on_hand
def __repr__(self):
    return f'InventoryItem(name={self.name!r}, unit_price={self.unit_price!r}, quantity_on_hand={self.quantity_on_hand!r})'
def __eq__(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) == (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented
def __ne__(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) != (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented
def __lt__(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) < (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented
def __le__(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) <= (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented
def __gt__(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) > (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented
def __ge__(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) >= (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented
```
といったメソッドが自動で生成される。

## namedtupleとの違い
* namedtupleは、偶然同じ数のfieldを持つ他のnamedtupleと比較できる。
	* Data Classでは、ここでFalseを返す。

```
>>> Point3D(2017, 6, 2) == Date(2017, 6, 2)
True
```
* namedtupleは、偶然tupleと比較できる。
	* Data Classでは、ここでFalseを返す

```
>>> Point2D（1,10）==（1,10）
True
```

* fieldを後から追加するのが難しい
* mutableにできない
* デフォルト値を指定できない
*  `__init__`や`__repr__`に使われるfiledを制御できない
* 継承によるfieldの結合ができない

## typing.NamedTupleとの違い
* 型アノテーションを利用できるが、結局はnamedtupleと同じ問題が残る

## attrsとの違い
* https://github.com/python-attrs/attrs
* attrsは、validatorsやconverters, metadataなどData Classにはない機能をサポートしている
* Data Classesはこれらの機能を実装しないことでシンプルに保っている
* 以下のissueで色々議論されている
	* https://github.com/ericvsmith/dataclasses/issues/19
	* attrsを標準に組み込むのはどう？という議論に対して、guidoが反対している

## Python 3.7 Release Schedule

予定では2018/6/15がPython3.7のリリース日。
Data Classesは使い勝手が良さそうなので、今から楽しみですね。

[PEP 537 -- Python 3.7 Release Schedule | Python.org](https://www.python.org/dev/peps/pep-0537)