Python
Python3

Python len()の中で何が起こっているのか

普段何気なく使っているlen()ですが、「長さを教えてくれる」程度の理解しかできていませんでした。ちょっと深堀ってみます。

※実行したバージョンはPython3.7.3です。


len()

関数の説明 公式リファレンスより


オブジェクトの長さ (要素の数) を返します。引数はシーケンス (文字列、バイト列、タプル、リスト、range 等) かコレクション (辞書、集合、凍結集合等) です。



使ってみる

リファレンス通り、シーケンスやコレクションを引数にする

# シーケンス型の例

>>> len('abc')
3
>>> len(['a','b'])
2

# コレクション型の例
# 辞書の場合はキーの数が返る
>>> len({'name':'サザエ','age':25})
2
>>> len({1,2,3})
3

シーケンスやコレクション以外のオブジェクトを引数にするとどうなるか

# TypeErrorになる

>>> len(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()
>>> len(True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'bool' has no len()

自作クラスのオブジェクトを引数にするとどうなるか


# intやboolを引数にしたときのようにTypeErrorになる
>>> class MyClass():
... pass
...
>>> len(MyClass())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'MyClass' has no len()


中で何が起こっているのか

len(hoge)が呼ばれると、内部でhoge.__len__()が呼ばれます。

__len__()は特殊メソッドの一種です。特殊メソッドについては公式リファレンスをご参照ください。(いつか特殊メソッドについてもまとめたい)

シーケンスやコレクションのオブジェクトでは__len__()が定義されています。

# 同じ結果になる

>>> len('abc')
3
>>> 'abc'.__len__()
3

しかし、intなどシーケンスやコレクションでないオブジェクトでは__len__()が定義されていません。

len()でシーケンスやコレクション以外のオブジェクトを引数にするとエラーになった理由はこれだったのです。

>>> int().__len__()

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute '__len__'

つまり、自作クラスのオブジェクトでも__len__()が定義されていればlen()の引数にできます。

>>> class MyClass2():

... def __len__(self):
... return 100
...
>>> obj = MyClass2()
>>> len(obj)
100


注意すべきこと

自作クラスの場合、__len__()の中身を自身で定義することができます。

極端な例ですが、以下のようなlistを継承して作った自作クラスMyListClassがあったとします。


>>> class MyListClass(list):
... # 要素のうち正の数の数を数えて、それを長さとする
... def __len__(self):
... cnt = 0
... for i in self:
... if i > 0:
... cnt += 1
... return cnt
...
>>> obj = MyListClass([1,2,-1])
>>> obj
[1, 2, -1]
# 正の数は2つなので、長さ2となる
>>> len(obj)
2

MyListClassの実装を知らずにlen()の引数にMyListClassオブジェクトを使用したら... 思わぬ不具合を生む可能性があります。

特殊メソッドを定義して動作をカスタマイズする場合、影響箇所を考慮する必要があります。また、自作クラスのオブジェクトを使用する場合はどういったカスタマイズがされているのか確認したうえで使用したほうがよさそうですね。