こんにちは!CryptoGamesというブロックチェーンゲーム企業でエンジニアをしているかるでねです!
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。
このブログ以外でも情報発信しているので、よければ他の記事も見ていってください。
https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58
今回はPythonの「特殊メソッド」について解説していきます。
Pythonの実務でよく他の人が書いたコードを読むことがあります。
その際結構な頻度で「特殊メソッド」が使われています。
慣れていないと何をしているのかわからず時間だけが過ぎていく可能性があります。
それを防ぐためにこの記事で「特殊メソッド」に慣れて、他の人が書いたコードをスラスラ読めるようになりましょう!
「Pythonの基礎はあらかたマスターした!」
「Pythonのクラスの知識を深めたい。」
「Pythonの特殊メソッドってなんだろう?」
「他の人のコードを読めるようになりたい。」
このような人に役に立てれば幸いです!
前置きは早々に本題に入っていきましょう。
この記事はPythonの「クラス」の理解をしておいた方が読みやすいです。「クラス」の理解が怪しいかたは以下を参考に学習してみてください。
はじめに
Pythonの「特殊メソッド」はめちゃくちゃたくさんあります。
その中でもよく使われるものを中心に紹介していきます。
もし今回紹介する以外の「特殊メソッド」を使いたい方は、公式ドキュメントを参考にしたりググってみてください。
また、この記事では丁寧な解説を心がけているので、同じようなコードが繰り返されます。
しつこく感じる方もいるかもしれませんが、初学者を対象に書いているのでご了承ください。
__new__
インスタンス生成。
概要
__new__
は「インスタンスオブジェクト」が生成される前に呼び出されます。
「インスタンスオブジェクト」とは、クラスを呼び出すことだと思ってもらえれば大丈夫です。
self
オブジェクトをインスタンス化し、第1引数cls
にクラスオブジェクトが代入されます。
「変更できないオブジェクトを変更したい」場面で使用されます。
では早速みていきましょう。
class Cardene:
def __new__(cls):
print("__new__")
print(f"cls: {cls}")
return super().__new__(cls)
def __init__(self):
print("__init__")
print(f"self: {self}")
cardene = Cardene()
2行目で定義して、引数にcls
を取っています。
出力を確認しましょう。
__new__
cls: <class '__main__.Cardene'>
__init__
self: <__main__.Cardene object at 0x10944cfd0>
__new__
の方はクラス自体で、次章で解説する__init__
はクラスオブジェクトになっているのが確認できますね。
インスタンス生成しない
ちなみにインスタンスを生成しないと__init__
が呼ばれません。
確認してみましょう。
class Cardene:
def __new__(cls):
print("__new__")
print(f"cls: {cls}")
# return super().__new__(cls)
def __init__(self):
print("__init__")
print(f"self: {self}")
cardene = Cardene()
5行目の部分をコメントアウトしました。
__new__
cls: <class '__main__.Cardene'>
先ほど呼ばれていた__init__
が呼ばれていませんね。
このように__new__
でインスタンスを生成していないと__init__
が呼ばれないので注意しましょう。
(__new__
を定義しない場合は自動でインスタンスが生成されます。)
イミュータブルオブジェクトを変更
「イミュータブル」とは、「変更できない」という意味です。
タプルなど一度定義した後に変更できないオブジェクトのことを指します。
通常クラスに渡された「イミュータブル」なオブジェクトは変更することができません。
まずはタプルから確認してみましょう。
numbers = (1, 2, 3)
print(numbers)
# (1, 2, 3)
ではこのタプルに変更を加えていきましょう。
numbers = (1, 2, 3)
numbers[1] = 4
print(numbers)
# TypeError: 'tuple' object does not support item assignment
想定通りエラーになりましたね。
では__init__
ないで変更を加えてみましょう。
class Cardene:
def __init__(self, num_tuple):
self.num_tuple = num_tuple
print(num_tuple)
self.num_tuple[1] = 4
cardene = Cardene((1, 2, 3))
5行目で先ほど同様4
を代入しようとしています。
TypeError: 'tuple' object does not support item assignment
先ほどと同じエラーが出ましたね。
では__new__
を使って解決していきましょう。
class Cardene(tuple):
def __new__(cls, num_tuple):
self = tuple.__new__(cls, (
num_tuple[0], 4, num_tuple[2]
))
print(self)
cardene = Cardene((1, 2, 3))
4行目でタプルを再定義しています。
(1, 4, 3)
変更できましたね!
「いやいや再定義してるじゃん!それなら別に__init__
でもできるじゃん!」
と思った方は鋭い!
ただこのような感じだったらどうでしょうか?
class Cardene(tuple):
def __new__(cls, num_tuple):
self = tuple.__new__(cls, (
num_tuple[0], 4, num_tuple[2]
))
print(self)
def __init__(self, num_tuple):
self.num_tuple = num_tuple
print(num_tuple)
cardene = Cardene((1, 2, 3))
先ほど同様__new__
を定義して、さらに__init__
も付け足してみました。
(1, 4, 3)
しっかり出力できましたね。
何が言いたいかというと、__init__
の時点でタプルの中身が変わっていますよね?
そのため実質タプルの中身を変更できたということです。
少し難しいですがこのように使用するので頭の片隅に入れておきましょう。
参考
__init__
インスタンス初期化。
前章でもみてきましたが、Pythonで「クラス」を使用する際にほぼ必須で使われる「特殊メソッド」です。
役割としてはインスタンスの初期化を行なってくれます。
インスタンスを複数生成した場合に、前の値が残っていては使いづらくて仕方がありません。
そのようなことが起きないように初期化を行なってくれます。
では早速確認してみましょう。
class Cardene:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
cardene = Cardene(10, 20)
print(f"num1: {cardene.num1}")
print(f"num2: {cardene.num2}")
# num1: 10
# num2: 20
2行目で__init__
を定義して、引数として受け取っているnum1
とnum2
をそれぞれself.num1
とself.num2
に入れています。
このself
はインスタンス自身のことで、今回の場合はself
=cardene
と思って貰えば理解しやすいと思います。
出力でもcardene.~
と指定することで出力できていますね。
インスタンス化される度に値が変わることを確認しましょう。
class Cardene:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
cardene = Cardene(10, 20)
print(f"num1: {cardene.num1}")
print(f"num2: {cardene.num2}")
cardene2 = Cardene(1000, 2000)
print(f"num1: {cardene2.num1}")
print(f"num2: {cardene2.num2}")
# num1: 10
# num2: 20
# num1: 1000
# num2: 2000
ちゃんと値が変わっていますね!
__del__
インスタンス破壊時に呼び出し。
こちらは前章までの「生成時」ではなく、「破壊時」に呼び出されます。
「デストラクタ」と呼ばれているのでこちらも一応覚えておきましょう。
滅多に使われませんが一応確認しましょう。
class Cardene:
def __init__(self, num):
self.num = num
def __del__(self):
print("インスタンスが破壊されたよ!")
cardene = Cardene(10)
print(f"num: {cardene.num}")
del cardene
5行目で「デストラクタ」を定義して、6行目でどんな処理をするかを書いています。
11行目でインスタンスを破壊しています。
num: 10
インスタンスが破壊されたよ!
ちゃんと__del__
で定義して処理が実行されていますね。
__str__
文字列呼び出し。
__str__
インスタンスを出力した際に呼び出されます。
まずは__str__
を定義していない方から確認してみましょう。
class Cardene:
def __init__(self, name, age):
self.name = name
self.age = age
cardene = Cardene("cardene", 20)
print(cardene)
# <__main__.Cardene object at 0x10b3dbaf0>
インスタンスオブジェクト情報が出力されいますね。
__str__
を定義するとこの出力が変わります。
ではみてみましょう。
class Cardene:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"私の名前は{self.name}です。\n年齢は{self.age}です。"
cardene = Cardene("cardene", 20)
print(cardene)
6行目と7行目で__str__
を定義しています。
では出力を確認してみましょう。
私の名前はcardeneです。
年齢は20です。
先ほどはオブジェクト情報が出力されていましたが、今回は__str__
で定義した文字列が出力されていますね。
このようにインスタンスをprint()
で出力した際に表示されるものを設定できます。
__repr__
文字列を呼び出し(型を戻せる)。
この__repr__
は先細野__str__
とほとんど同じです。
まずはこのrepr
がどういったものなのかを確認してきましょう。
rper
import datetime
today = datetime.datetime.today()
print(str(today))
print(type(str(today)))
print(repr(today))
print(type(repr(today)))
str
の出力とrper
の出力を比較していきます。
2022-02-25 12:13:30.371320
<class 'str'>
datetime.datetime(2022, 2, 25, 12, 13, 30, 371320)
<class 'str'>
どちらもデータ型はstr
ですが出力が違いますね。
rper
の方はdatetime.datetime
と余分なものがついています。
これは型をもとに戻す際に役立ちます。
import datetime
today = datetime.datetime.today()
print(eval(repr(today)))
print(type(eval(repr(today))))
print(eval(str(today)))
print(type(eval(str(today))))
eval
というものを使用して型をもとに戻してみます。
2022-02-25 12:15:47.573769
<class 'datetime.datetime'>
...
SyntaxError: leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers
rper
の方はデータ型がdatetime
に戻っていますが、str
の方はデータ型をモドに戻せずエラーになっています。
このようにデータ型をもとに戻せるということを覚えておきましょう。
__rper__の確認
では本題に戻って__rper__
をみていきましょう。
この__rper__
は主にJupyter Notebookなどを使用している際に使われます。
どのように使われているのかみていきましょう。
class Cardene:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"str: 私の名前は{self.name}です。\n年齢は{self.age}です。"
def __repr__(self):
return f"repr: 私の名前は{self.name}です。\n年齢は{self.age}です。"
cardene = Cardene("cardene", 20)
print(cardene)
cardene
6行目と7行目で__str__
を定義し、9行目と10行目で__rper__
を定義しています。
13行目と14行目の出力部分はそれぞれprint()
で囲ったものと、囲んでいないものの2つがあります。
では出力を確認していきましょう。
str: 私の名前はcardeneです。
年齢は20です。
repr: 私の名前はcardeneです。
年齢は20です。
どちらも出力できましたね。
ちなみになのですが、Jupyter Notebookではprint()
で囲わなくても出力されます。
__rper__
はprint()
を使っても使わなくても呼び出すことができます。
補足としては、__str__
の方が__rper__
よりも優先して呼び出されます。
比較
次に数値の比較をした際に呼び出される「特殊メソッド」についてみていきましょう。
数値の比較とは、<
や=
のことです。
__lt__
より小さい。
less than
の略で、「〜より小さいか」を判定してくれます。
class Cardene:
def __init__(self,num):
self.num = num
def __lt__(self, other):
print("less than")
return self.num < other.num
cardene_10 = Cardene(10)
cardene_20 = Cardene(20)
print(cardene_10 < cardene_20)
less than
True
<
で2つのインスタンスを比較することで、__lt__
が呼び出されていますね。
__le__
その値以下。
less than or equal
の略で、「〜以下か」を判定してくれます。
class Cardene:
def __init__(self,num):
self.num = num
def __le__(self, other):
print("less than or qual")
return self.num <= other.num
cardene_10 = Cardene(10)
cardene_20 = Cardene(20)
print(cardene_10 <= cardene_20)
less than or qual
True
<=
で2つのインスタンスを比較することで、__le__
が呼び出されていますね。
__eq__
等しい。
equal
の略で、「〜と等しいか」を判定してくれます。
class Cardene:
def __init__(self,num):
self.num = num
def __eq__(self, other):
print("equal")
return self.num == other.num
cardene_10 = Cardene(10)
cardene_20 = Cardene(20)
print(cardene_10 == cardene_20)
equal
False
==
で2つのインスタンスを比較することで、__eq__
が呼び出されていますね。
__ne__
等しくない。,
not equal
の略で、「〜と等しくないか」を判定してくれます。
class Cardene:
def __init__(self,num):
self.num = num
def __ne__(self, other):
print("not equal")
return self.num != other.num
cardene_10 = Cardene(10)
cardene_20 = Cardene(20)
print(cardene_10 != cardene_20)
not equal
True
!=
で2つのインスタンスを比較することで、__ne__
が呼び出されていますね。
__gt__
より大きい。
greater than
の略で、「〜より大きいか」を判定してくれます。
class Cardene:
def __init__(self,num):
self.num = num
def __gt__(self, other):
print("greater than")
return self.num > other.num
cardene_10 = Cardene(10)
cardene_20 = Cardene(20)
print(cardene_10 > cardene_20)
greater than
False
>
で2つのインスタンスを比較することで、__gt__
が呼び出されていますね。
__ge__
その値以上。
greater than or equal
の略で、「〜以上か」を判定してくれます。
class Cardene:
def __init__(self,num):
self.num = num
def __ge__(self, other):
print("greater than or equal")
return self.num >= other.num
cardene_10 = Cardene(10)
cardene_20 = Cardene(20)
print(cardene_10 >= cardene_20)
greater than or equal
False
<=
で2つのインスタンスを比較することで、__ge__
が呼び出されていますね。
まとめ
まとめとして一気に確認してみましょう。
class Cardene:
def __init__(self,num):
self.num = num
def __lt__(self, other):
print("less than")
return self.num < other.num
def __le__(self, other):
print("less than or qual")
return self.num <= other.num
def __eq__(self, other):
print("equal")
return self.num == other.num
def __ne__(self, other):
print("not equal")
return self.num != other.num
def __gt__(self, other):
print("greater than")
return self.num > other.num
def __ge__(self, other):
print("greater than or equal")
return self.num >= other.num
cardene_10 = Cardene(10)
cardene_20 = Cardene(20)
print(cardene_10 < cardene_20)
print(cardene_10 <= cardene_20)
print(cardene_10 == cardene_20)
print(cardene_10 != cardene_20)
print(cardene_10 > cardene_20)
print(cardene_10 >= cardene_20)
less than
True
less than or qual
True
equal
False
not equal
True
greater than
False
greater than or equal
False
ちゃんと呼び出せましたね!
参考
演算
この章では演算を行う「特殊メソッド」についてみていきます。
前章と同じように使用するのでそこまで難しくないはずです!
早速みていきましょう。
__add__
足し算。
足し算を行う「特殊メソッド」です。
class Cardene:
def __init__(self,num):
self.num = num
def __add__(self,other):
print("足し算")
return self.num + other.num
cardene1 = Cardene(10)
cardene2 = Cardene(20)
print(cardene1 + cardene2)
足し算
30
ちゃんと足し算できていますね。
__sub__
引き算。
引き算を行う「特殊メソッド」です。
class Cardene:
def __init__(self,num):
self.num = num
def __sub__(self,other):
print("引き算")
return self.num + other.num
cardene1 = Cardene(10)
cardene2 = Cardene(20)
print(cardene1 - cardene2)
引き算
-10
ちゃんと引き算できていますね。
__mul__
掛け算。
掛け算を行う「特殊メソッド」です。
class Cardene:
def __init__(self,num):
self.num = num
def __mul__(self,other):
print("掛け算")
return self.num * other.num
cardene1 = Cardene(10)
cardene2 = Cardene(20)
print(cardene1 * cardene2)
掛け算
200
ちゃんと掛け算できていますね。
__truediv__
割り算(四捨五入なし)。
割り算を行う「特殊メソッド」です。
class Cardene:
def __init__(self,num):
self.num = num
def __truediv__(self,other):
print("割り算")
return self.num / other.num
cardene1 = Cardene(10)
cardene2 = Cardene(20)
print(cardene1 / cardene2)
割り算
0.5
ちゃんと割り算できていますね。
__floordiv__
割り算(四捨五入あり)。
割り算を行う「特殊メソッド」です。
class Cardene:
def __init__(self,num):
self.num = num
def __floordiv__(self,other):
print("割り算")
return self.num // other.num
cardene1 = Cardene(10)
cardene2 = Cardene(20)
print(cardene1 // cardene2)
割り算
0
ちゃんと割り算できていますね。
まとめ
最後に演算を行う「特殊メソッド」をまとめて復習しましょう。
class Cardene:
def __init__(self,num):
self.num = num
def __add__(self,other):
print("足し算")
return self.num + other.num
def __sub__(self,other):
print("引き算")
return self.num + other.num
def __mul__(self,other):
print("掛け算")
return self.num * other.num
def __truediv__(self,other):
print("割り算")
return self.num / other.num
def __floordiv__(self,other):
print("割り算")
return self.num // other.num
cardene1 = Cardene(10)
cardene2 = Cardene(20)
print(cardene1 + cardene2)
print(cardene1 - cardene2)
print(cardene1 * cardene2)
print(cardene1 / cardene2)
print(cardene1 // cardene2)
足し算
30
引き算
30
掛け算
200
割り算
0.5
割り算
0
ビット演算子
この章では「ビット演算子」についてみていきます。
「ビット演算子」とは0
と1
の組み合わせで出力が決まるものです。
(1
は正、0
は偽と認識することもできます。)
and
まずはand
から見ていきましょう。
and
は値が全て1
の時1
を返し、1つでも0
が含まれていたら0
を返します。
class Cardene:
def __init__(self,num):
self.num = num
def __and__(self,other):
print("and")
return self.num & self.num
cardene1 = Cardene(1)
cardene2 = Cardene(0)
print(cardene1 & cardene2) # 1 & 0
print(cardene1 & cardene1) # 1 & 1
print(cardene2 & cardene2) # 0 & 0
12行目から14行目までの複数のパターンで試してみます。
and
0
and
1
and
0
ちゃんと説明した通りの出力になっていますね。
or
まずはor
から見ていきましょう。
or
は1つでも0
が含まれていたら1
を返します。
class Cardene:
def __init__(self,num):
self.num = num
def __or__(self,other):
print("or")
return self.num | self.num
cardene1 = Cardene(1)
cardene2 = Cardene(0)
print(cardene1 | cardene2) # 1 & 0
print(cardene1 | cardene1) # 1 & 1
print(cardene2 | cardene2) # 0 & 0
12行目から14行目までの複数のパターンで試してみます。
or
1
or
1
or
0
ちゃんと説明した通りの出力になっていますね。
最後に
今回はPythonの「特殊メソッド」についてみてきました。
GitHubに上がっているコードや他の人が書いたコードを見る際に「特殊メソッド」をよく見かけます。
この機会にしっかり「特殊メソッド」について理解して、他の人が書いたコードを読んでいる際に「特殊メソッド」が出てきても楽々読み進められるようにしましょう!
普段はPythonやブロックチェーンメインに情報発信をしています。
Twiiterでは図解でわかりやすく解説する投稿をしているのでぜひフォローしてくれると嬉しいです!
Tweets by cardene777