特殊メソッドしたい(__dunder__

Pythonで書かれたソースコードを読んでいると、__で囲まれたメソッド名が目に入ってきます。 これは__特殊メソッド__を意味していて、ふたつのアンダースコアが連なるため**ダンダー(dunder)**と呼ばれてます。

また、ひとつのアンダースコアを変数名の前につけて内部変数を意味したり、 変数名の後ろにつけて仮変数とする慣習もあります。

初期化したい(__init__

1class Point:
2    def __init__(self, x, y):
3        self.x = x
4        self.y = y
5
6p1 = Point(1, 2)

__init__は、インスタンスを生成するときに自動的に呼ばれる特殊メソッドです。 Point(1, 2)のようにクラスを呼び出すと、 内部で__init__が呼ばれ、self.xself.yに値が設定されます。 C++やJavaでいう「コンストラクタ」に相当します。

文字列にしたい(__str__ / __repr__

 1class Point:
 2    def __init__(self, x, y):
 3        self.x = x
 4        self.y = y
 5
 6    def __str__(self):
 7        return f"Point({self.x}, {self.y})"
 8
 9    def __repr__(self):
10        return f"Point(x={self.x!r}, y={self.y!r})"
11
12p1 = Point(1, 2)
13print(p1)   # Point(1, 2)          <- __str__が呼ばれる
14p1          # Point(x=1, y=2)      <- 対話モードでは__repr__が呼ばれる

__str____repr__を実装すると、 str()print()で自作クラスを文字列として表示できるようになります。 役割は似ていますが、意図が異なります。

  • __str__:人間が読みやすい表示を意図する

  • __repr__:開発者向けの、できればeval()で再現できるような表示を意図する

ヒント

__str__が定義されていない場合は、__repr__が代わりに使われます。

比較演算子したい(__eq__ / __lt__ / __gt__

 1class Point:
 2    def __init__(self, x, y):
 3        self.x = x
 4        self.y = y
 5
 6    def __eq__(self, other):
 7        return (self.x, self.y) == (other.x, other.y)
 8
 9    def __lt__(self, other):
10        return (self.x, self.y) < (other.x, other.y)
11
12p1 = Point(1, 2)
13p2 = Point(1, 2)
14p3 = Point(3, 4)
15
16print(p1 == p2)   # True   <- __eq__が呼ばれる
17print(p1 < p3)     # True   <- __lt__が呼ばれる

比較演算子にも、それぞれ対応する特殊メソッドがあります。

演算子

特殊メソッド

==

__eq__

!=

__ne__

<

__lt__

<=

__le__

>

__gt__

>=

__ge__

注釈

__eq__を定義すると、デフォルトのハッシュ値が使えなくなり、 そのままではsetや辞書のキーとして使えなくなります(TypeError: unhashable type)。 必要であれば__hash__も一緒に定義してください。

四則演算したい(__add__ / __sub__

 1class Point:
 2    def __init__(self, x, y):
 3        self.x = x
 4        self.y = y
 5
 6    def __add__(self, other):
 7        return Point(self.x + other.x, self.y + other.y)
 8
 9    def __sub__(self, other):
10        return Point(self.x - other.x, self.y - other.y)
11
12    def __mul__(self, scalar):
13        return Point(self.x * scalar, self.y * scalar)
14
15    def __str__(self):
16        return f"Point({self.x}, {self.y})"
17
18p1 = Point(1, 2)
19p2 = Point(3, 4)
20
21print(p1 + p2)   # Point(4, 6)   <- __add__が呼ばれる
22print(p1 - p2)   # Point(-2, -2) <- __sub__が呼ばれる
23print(p1 * 2)     # Point(2, 4)   <- __mul__が呼ばれる

四則演算にも、それぞれ対応する特殊メソッドがあります。

演算子

特殊メソッド

+

__add__

-

__sub__

*

__mul__

/

__truediv__

//

__floordiv__

%

__mod__

長さしたい(__len__

1class Group:
2    def __init__(self, members):
3        self.members = members
4
5    def __len__(self):
6        return len(self.members)
7
8group = Group(["Alice", "Bob", "Carol"])
9print(len(group))   # 3   <- __len__が呼ばれる

__len__を実装すると、len()で自作クラスの「長さ」を取得できるようになります。 戻り値は0以上の整数(int)である必要があります。

ヒント

__len__を実装すると、そのクラスのインスタンスはbool()if文で len() == 0のときにFalseと判定されるようになります (__bool__が定義されていない場合)。

プライベート変数したい(_変数名 / _関数名()

1def _internal_function(引数):
2    pass

変数名や関数名の先頭に_をつけて、プライベートであることを表します。

注意

Pythonの仕組み的に、C++のようなプライベート変数はありません。 先頭に_をつけても、言語レベルでのアクセス制限は一切かかっておらず、 モジュール名._変数名のようにふつうにアクセスできてしまいます。 ヒトの目に「触れてはいけない」と伝えるための、あくまで慣習上の印です。

呼び方と実装が一致していないので、C++などから来たひとは戸惑うかもしれませんが、 個人的には、セッター/ゲッターを書かなくてもいいので便利だなと感じます。

一時変数したい(_

1for i in range(10):
2    time.sleep(1)
3
4# i を使わないので、次のように書いてもOK
5for _ in range(10):
6    time.sleep(1)

返り値を受け取る必要がない場合、_で代用できます。

仮変数したい(変数名_

1type_ = type(変数)

使いたい変数名が、Pythonの予約語と重なってしまう場合は、変数名のあとに_をつける慣習があります。 できるかぎり命名規則に沿った変数名を考えるべきですが、 スコープの範囲が狭かったり、一時的な変数の場合には、このようにしてもよいと思います。