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]