Pythonエンジニア勉強会
2021/04/27 nikkie
dataclass使ったことありますか?
「特殊メソッド」ピンと来ますか?
Python界隈では 自己紹介のエイリアス が「お前、誰よ」(ルーツ) 活用ください!
「Python」について、活用ユーザーが集まり最新トレンド共有やLTを行う勉強会イベント
Pythonのカンファレンス PyCon JP
YouTube PyCon JP 2020 アーカイブ動画
nikkieはPyCon JP 2021 座長 🇨🇭です。スタッフ募集中!📣🙏
みんなのPython勉強会 6/9(水) は登壇者公募中!(フォーム)
トークテーマは『あなたのオススメPython本』📚
持ち時間は 10分 で、短くてもOK
dataclass使ったことありますか?
「特殊メソッド」ピンと来ますか?
→ nikkieはこれらを活用し始められて、Python書くのがすごく 楽しい !😆
Python活用事例や最新トレンドではありません🙇♂️(私も知りたい!)
Pythonを活用するために Pythonの裏側を知りましょう
この話が「当たり前」という人は、Pythonを活用できていると思います!
動作環境:Python 3.9.4
nikkieの過去のアウトプットから
PyCon Taiwan 2020で発表 PyCon JP スタッフ活動でのPython活用事例 (英語)
2020年Remote.pyで発表「Pythonで自動化スクリプトを作るときに考えていること」
Pythonのobject
objectどうしが「等しい」
特殊メソッドでobjectを反復できる
Pythonのobject
objectどうしが「等しい」
特殊メソッドでobjectを反復できる
Python における オブジェクト (object) とは、データ を抽象的に表したものです。
Python 言語リファレンス - 3. データモデル - 3.1 . オブジェクト、値、および型 (強調は引用者による)
Python プログラムにおける データ は全て、オブジェクトまたはオブジェクト間の関係として表されます。
Python 言語リファレンス - 3. データモデル - 3.1 . オブジェクト、値、および型 (強調は引用者による)
Pythonのオブジェクト(object) = データ という話をしています
示唆を与えてくださったブログ:オブジェクト指向という言葉を使わずともコードは書ける
状態 (属性 や値) と定義された振る舞い (メソッド) をもつ全てのデータ
https://docs.python.org/ja/3/glossary.html#term-object (強調は引用者による)
もしくは、全ての 新スタイルクラス の究極の 基底クラス のこと。
class SomeClass: # クラスはobjectを継承している
pass
属性とメソッドを持つ データ
どんなクラスも object を継承(究極の 基底クラス)
👉 ここからはPythonにおけるデータの 振る舞い について話していきます
Pythonのobject
objectどうしが「等しい」
特殊メソッドでobjectを反復できる
次のデータ(馬🐴の名を表すobject)は等しい?
>>> class RaceHorseName:
... """競走馬の名前を表す"""
... def __init__(self, value):
... self.value = value
>>> rice = RaceHorseName("ライスシャワー")
>>> rice2 = RaceHorseName("ライスシャワー")
>>> rice == rice2
馬の名は、等しくない
>>> rice.value == rice2.value # value属性の値は等しい
True
>>> rice is rice2 # オブジェクトは同一ではない
False
>>> rice == rice2
False
==
x==yはx.__eq__(y)を呼び出します
https://docs.python.org/ja/3/reference/datamodel.html#object.__eq__
__eq__ メソッド?RaceHorseName には実装していない
究極の基底クラス object が __eq__ を持つ
その実装は True if x is y else NotImplemented (ドキュメント)
is:同一性の比較
x is yは、 x と y が 同じオブジェクトを指す とき、かつそのときに限り真になります。 オブジェクトの同一性は id() 関数を使って判定されます。
https://docs.python.org/ja/3/reference/expressions.html#is (強調は引用者による)
>>> rice is rice # オブジェクトは同一
True
rice == rice2 は、オブジェクトの 同一性 を比較する結果になった
rice と rice2 は 別々のオブジェクトを指す ので、False
(組み込み関数 id で、別々のオブジェクトを指していることを確認できます)
value 属性の 値が等しい とき、データ(object)は等しく したい(ライスシャワーはライスシャワー)
👉 __eq__ メソッドを RaceHorseName クラスで実装する(object の __eq__ を オーバーライド)
__eq__ メソッド オーバーライド
>>> class RaceHorseName:
... def __init__(self, value):
... self.value = value
... def __eq__(self, other):
... if not isinstance(other, self.__class__):
... return NotImplemented # 👉 Appendix
... return self.value == other.value
__eq__ メソッド オーバーライド
>>> rice = RaceHorseName("ライスシャワー")
>>> rice2 = RaceHorseName("ライスシャワー")
>>> rice == rice2 # value属性が等しいときに、データとして等しくできた
True
>>> rice is rice2
False
>>> from dataclasses import dataclass
>>> @dataclass
... class RaceHorseName:
... value: str
>>> rice = RaceHorseName("ライスシャワー")
>>> rice2 = RaceHorseName("ライスシャワー")
>>> rice == rice2
True
@dataclasses.dataclass「dataclass使ったことありますか?」はこれを指していました
クラスに付けるデコレータ(用語集)
RaceHorseName クラスに __eq__ を作り、object の __eq__ をオーバーライド
@dataclass
class RaceHorseName1:
...
@dataclass()
class RaceHorseName2:
...
@dataclasses.dataclass の eq 引数
eq: (デフォルトの)真の場合、 __eq__() メソッドが生成されます。このメソッドはクラスの比較を、そのクラスの フィールドからなるタプルを比較 するように行います。 比較する2つのインスタンスのクラスは同一でなければなりません。
https://docs.python.org/ja/3/library/dataclasses.html#dataclasses.dataclass (強調は引用者による)
@dataclasses.dataclass によってRaceHorseName クラスに __eq__メソッドが作られた
この __eq__ では、クラスが同じことと (self.value, ) を比較
👉 クラスが同じで、上記タプルが等しい ので、rice == rice2 は True と評価された
クラスに __eq__ メソッドを実装して、object の __eq__ をオーバーライドすればいい
@dataclasses.dataclass でクラスをデコレートすると __eq__ メソッドが作られて、少ない記述で済む 🙌
Pythonのobject
objectどうしが「等しい」
特殊メソッドでobjectを反復できる
__eq__ などのメソッドのこと(特殊メソッド名一覧)
究極の基底クラス object で定義されていて、オーバーライド することで データの振る舞いをカスタマイズ できる
マジックメソッド、ダンダーメソッドとも呼ばれる(👉 Appendix)
RaceHorseName クラスのデータをいくつも持たせられるクラス RaceHorseNames
rice = RaceHorseName("ライスシャワー")
bourbon = RaceHorseName("ミホノブルボン")
names = RaceHorseNames([rice, bourbon])
for name in names:
print(f"{name.value}さん、こんにちは")
今回は Sequence というクラスを継承 して、特殊メソッドをオーバーライドして作成
for 文で繰り返したいだけであれば、Iterable(用語集)になればいいので、他の特殊メソッドをオーバーライドしてもできます
リスト、タプル、文字列もシーケンス
整数インデクスによる効率的な要素アクセスを
__getitem__()特殊メソッドを通じてサポートし、長さを返す__len__()メソッドを定義した iterable です
以下の特殊メソッドを実装
__len__
__getitem__
オススメ:抽象基底クラスを継承 する
collections.abc.Sequence を継承する
継承することで、__len__、__getitem__ の 実装が強制される
オススメ理由:実装する特殊メソッドを覚えておくより、継承する抽象基底クラスを覚えておくほうが覚える量が少ない(※個人の見解です)
>>> from collections.abc import Sequence
>>> @dataclass
... class RaceHorseNames(Sequence):
... names: list[RaceHorseName] # 👉 Appendix
>>> names = RaceHorseNames([])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class RaceHorseNames with abstract methods __getitem__, __len__
__len__オブジェクトの長さ を 0 以上の整数で返さなければなりません。
https://docs.python.org/ja/3/reference/datamodel.html#object.__len__ (強調は引用者による)
__len__ を実装
>>> @dataclass
... class RaceHorseNames(Sequence):
... names: list[RaceHorseName]
... # リストnamesの長さをオブジェクトの長さとする
... def __len__(self):
... return len(self.names)
__getitem__self[key] の値評価 (evaluation) を実現するために呼び出されます。 シーケンスの場合、キーとして整数とスライスオブジェクトを受理 できなければなりません。
https://docs.python.org/ja/3/reference/datamodel.html#object.__getitem__ (強調は引用者による)
__getitem__ を実装
>>> @dataclass
... class RaceHorseNames(Sequence):
... names: list[RaceHorseName]
... # -- __len__ は省略 --
... # namesはリストなので、整数もスライスも受け付けられる
... def __getitem__(self, key):
... if isinstance(key, slice):
... return self.__class__(self.names[key])
... return self.names[key]
>>> rice = RaceHorseName("ライスシャワー")
>>> bourbon = RaceHorseName("ミホノブルボン")
>>> names = RaceHorseNames([rice, bourbon])
>>> for name in names:
... print(f"{name.value}さん、こんにちは")
ライスシャワーさん、こんにちは
ミホノブルボンさん、こんにちは
クラスに __len__、__getitem__ を実装した
Sequence を継承 することで、実装が強制される(推奨納言)
Pythonのデータ (object) の振る舞いのカスタマイズの一例
Pythonのobjectはデータであり、究極の基底クラス
object が持つ 特殊メソッドをオーバーライド して、データの振る舞いをカスタマイズ 😆
@dataclass で特殊メソッド作成
抽象基底クラスを継承して特殊メソッド実装
Enjoy development with object!
References、Appendix が続きます(よろしければどうぞ!)
PyCon JP 2017 Pythonはどうやってlen関数で長さを手にいれているの?
このLTと特に関係するのは、スライド43。このトーク自体オススメです
『ゼロから作るDeep Learning ❸――フレームワーク編』
__call__ , __add__ , __radd__ が紹介されていて興味を持ちました
object の __eq__究極の基底クラス object の __eq__ の実装
True if x is y else NotImplemented と共有しました
同値性: 同じ 値 (同一でなくても値として同じになりうる)
同一性: is で比較。同じ オブジェクト (同じ id)
このLTでは、__eq__ をオーバーライドして同値性を比較できるようにした
NotImplemented単一の値しかない型(ref: 標準型の階層)
NotImplementedError ではないので注意
__eq__ で NotImplemented を return== の評価は False (そうなるための仕組みが NotImplemented)
>>> class RaceHorseName:
... def __init__(self, value):
... self.value = value
... def __eq__(self, other):
... if not isinstance(other, self.__class__):
... return NotImplemented
... return self.value == other.value
>>> rice = RaceHorseName("ライスシャワー")
>>> rice == "ライスシャワー"
False
@dataclass
@dataclass()
nikkieは上の方で使うことが多いです
dataclasses のドキュメントよりdataclass() が引数を指定しない単純なデコレータとして使用された場合、ドキュメントに記載されているシグネチャの デフォルト値のとおり に動作します。
https://docs.python.org/ja/3/library/dataclasses.html#dataclasses.dataclass (強調は引用者による)
特殊メソッド
マジックメソッド
ダンダーメソッド
special method の訳語
(LT本編で解説したとおり)
https://docs.python.org/ja/3/glossary.html#term-special-method
special method のくだけた同義語です。
https://docs.python.org/ja/3/glossary.html#term-magic-method
"ダンダー" メソッド (訳注:dunderはdouble underscoreの略で、メソッド名の前後にアンダースコアが2つ付いているメソッド)
https://docs.python.org/ja/3/library/dataclasses.html#dataclasses.dataclass
以下のコードはPython3.8以前では動きません
>>> @dataclass
... class RaceHorseNames(Sequence):
... names: list[RaceHorseName]
TypeError: 'type' object is not subscriptable
Python3.9で list など組み込み型が Generic型 になった
詳しくは過去のアウトプット:PEP 585, type hinting generics in standard collections について
>>> from typing import List
>>> @dataclass
... class RaceHorseNames(Sequence):
... names: List[RaceHorseName]
typing.List はPython3.9でdeprecated
>>> from __future__ import annotations
>>> @dataclass
... class RaceHorseNames(Sequence):
... names: list[RaceHorseName]