Pythonはどうやってlen関数で長さを手にいれているの?
2024.11.16
清水川 貴之
1
2
Pythonはどうやってlen関数で長さを手にいれているの?
How does python get length with the len() function?
3
Target attendees
以下のように思っている方
4
アジェンダ
5
len() がオブジェクトの長さを手に入れる方法
初級
len()関数に文字列を渡して起こること
7
>>> len("もじれつ")
4
>>> "もじれつ".__len__()
4
len()要らないのでは?
8
"もじれつ"
__len__
4
4
return
len()要らないのでは?
9
"もじれつ"
len()
__len__
4
int?
TypeError
4
return
Yes
No
__len__() がint以外の値を返すと..
len関数で、�型と値をチェック!
10
>>> class WaruiObj:
... def __len__(self):
... return 1.2
...
>>> w = WaruiObj()
>>> len(w)
Traceback (most recent call last):
File "<python-input-3>", line 1, in <module>
len(w)
~~~^^^
TypeError: 'float' object cannot be interpreted as an integer
__len__
TypeError
return
Yes
No
int?
len()
sys.maxsize を超える値を返すとOverflowError例外を起こします。
― データモデル より
len()関数の役割
11
"もじれつ"
len()
int?
TypeError
Yes
No
__len__
__len__
4
4
return
Adapter Pattern!!
Adapter Pattern とは
12
Adapter Pattern(アダプター・パターン)とは、GoF (Gang of Four; 4人のギャングたち) によって定義されたデザインパターンの1つである。Adapter パターンを用いると、既存のクラスに対して修正を加えることなく、インタフェースを変更することができる。
Interface
交流 100V~240V
直流 20V 3.25A
Protocol
― Wikipedia より
どのオブジェクトにも使えるlen() Adapter
13
任意のobject
len()
__len__
TypeError
return
Yes
No
__len__
Interface
Interface
Protocol
プロトコル
異常?
listに len() Adapter
14
list:
[2, None, ‘Yo’]
len()
__len__
3
int?
TypeError
3
return
Yes
No
__len__
dictに len() Adapter
15
dict: {
"age": 999,
"name": "Hoge"�}
len()
__len__
2
int?
TypeError
2
return
Yes
No
__len__
独自のデータ型に len() Adapter
16
>>> import random
>>> class Random:
... def __len__(self):
... return random.randint(0, 10)
...
>>> r = Random()
>>> len(r)
10
>>> len(r)
0
>>> len(r)
5
__len__
TypeError
return
Yes
No
int?
len()
?
__len__
独自データ型
Protocol: オブジェクトの振る舞い
17
任意のobject
len()
__len__
int?
TypeError
return
Yes
No
__len__
Interface
Protocol
Protocol ってどこに書いてあるの?
18
__len__
__len__
Protocol
Protocolの定義はどこにあるの?
19
__len__
__len__
Protocol
PEPはPython拡張提案(Python Enhancement Proposal)を表しています。PEPはPythonのコミュニティに対して情報を提供したり、Pythonの新機能やプロセス、環境などを説明するための設計書です。PEPは、技術的な仕様と、その機能が必要な論理的な理由を提供しなければなりません。
― PEP-1 より
Protocol一覧の代わりに
20
ここまでのまとめ
len() は Adapter
オブジェクトとAdapterが�通信する規約がプロトコル
21
Adapter、値のチェックしてるだけでしょ?
22
len()
int?
TypeError
Yes
No
__len__
4
4
return
len() 、 max() 、 min() を組み込み関数として実装することで、それぞれの型のメソッドとして実装するより少ないコードで済みます。
― デザインと歴史 FAQ より
こういうメリットもあるよ
次はもうちょっと複雑な例
(´・ω・`)ノ
23
if がオブジェクトのTrue/Falseを判断する方法
初級++
if 文のルール
25
if obj:
print("Trueだ!")�else:� print("Falseだ!")
if bool(obj):
bool()関数に数値を渡したときに起こること
26
>>> bool(123)
True
>>> (123).__bool__()
True
bool()関数に文字を渡したときに起こること
27
>>> bool("もじれつ")
True
>>> "もじれつ".__bool__()
Traceback (most recent call last):
File "<python-input-5>", line 1, in <module>
"もじれつ".__bool__()
^^^^^^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute '__bool__'
数値や文字を bool() に変換するRule
28
偽と見なされる条件
クラスが __bool__() または __len__() メソッドを定義していれば、それらのメソッドが整数 0 または bool 値 False を返すとき。
真と見なされる条件
偽じゃないやつ
― 真偽値判定 より
bool() は len() よりも仕事してそう
bool() Adapter
29
任意のobject
len()
bool()
Yes
__bool__
return
__len__
メソッドある?
No
Yes
bool型?
No
bool(value)
or
CPython での bool() の実装コード�https://github.com/python/cpython/blob/v3.13.0/Objects/typeobject.c#L9366-L9415
TypeError
独自のデータ型に bool() Adapter
30
>>> class PositiveInt(int):
... def __bool__(self):
... return self > 0
...
>>> bool(PositiveInt(10))
True
>>> bool(PositiveInt(-3)) # 0以下の値はFalse
False
>>> bool(-3) # 本来のintはマイナス値もTrue
True
さらにレベル上げていくよー
(`・ω・´)
31
for がオブジェクトの�繰り返し要素を取得する方法
初級++++
for 文のルール
33
for o in obj:
print(o)
for o in iter(obj):
object を iter() に変換するルール
34
iter(object) は イテレータ (iterator) オブジェクトを返します。object は反復プロトコル (__iter__() メソッド) か、シーケンスプロトコル (引数が 0 から開始する __getitem__() メソッド) をサポートする集合オブジェクトでなければなりません。これらのプロトコルが両方ともサポートされていない場合、 TypeError が送出されます。
― 組み込み関数 iter() より
bool() よりずっと大変そう
iter() Adapter
35
任意のobject
iter()
Yes
__iter__
return
メソッド�ある?
No
Yes
No
イテレータ?
__getitem__
0から順番に obj.__getitem__()に渡して、IndexErrorが発生するまで繰り返すiterator実装を提供する
iterator
or
Iterator Pattern!!
参考: Wikipedia
TypeError
iter() が返すIteratorとは
36
イテレータ(iterator)は、データの流れを表現するオブジェクトです。イテレータの __next__() メソッドを繰り返し呼び出す (または組み込み関数 next() に渡す) と、流れの中の要素を一つずつ返します。データがなくなると、代わりに StopIteration 例外を送出します。
― 組み込み関数 iter() より
イテレータオブジェクト自体は以下の 2 つのメソッドをサポートする必要があります。これらのメソッドは 2 つ合わせて iterator protocol: (イテレータプロトコル) を成します … __next__(), __iter__()
― 用語集 iterator より
for 文のルール(もうちょっと正確に)
37
for o in obj:
print(o)
it = iter(obj)
while True:
try:
o = next(it)
except StopIteration:
break
print(o)
はい、iter() 関数 Adapter と next() 関数 Adapterです。
next() Adapter と iterator
38
iterator
対象オブジェクトから位置カウンタを使って値を取り出して返す
iterator自体を返す
next()
return
値
__next__
__iter__
and
iteratorの実装例
39
class MyIterator:
def __init__(self, obj):
self.obj = obj
self.c = 0
def __next__(self):
try:
r = self.obj[self.c]
self.c += 1
return r
except IndexError:
raise StopIteration
def __iter__(self):
return self
next()
return
値
__next__
__iter__
and
for 文の1行目で起こっていること
この1行で色々起きてます
40
任意の
object
__iter__
__getitem__
or
next()
return
値
__next__
__iter__
iter()
and
iterator
object
return
for o in obj:
iterator protocol
組み込み型 イテレータ型 より
Protocol?
特に名称はなさそう(´・ω・`)
独自のデータ型に iter(), next() Adapter
41
class MyContainer:
def __init__(self, mapping):
self.keys = sorted(mapping) # ソートして保持
self.mapping = mapping # 値返し用
def __iter__(self): # for文で呼ばれる
return MyIterator(self)
def __getitem__(self, idx): # MyIteratorから呼ばれる
return self.mapping[self.keys[idx]]
>>> list(MyContainer({'foo': 1, 'bar': 2, 'poke': 3, 'ah': 4}))
[4, 2, 1, 3]
Iterator Protoclの抽象基底クラス
42
継承によるProtocolの強制
43
from collections.abc import Iterator
class MyIterator(Iterator):
pass
>>> MyIterator()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyIterator with abstract methods __next__
Explicit is better than implicit.
(´・ω・)おつかれさま(・ω・`)
44
まとめ
まとめ
46
公式リファレンスに�多くの情報が載っている
原典を調べよう
PEPを読んでみよう
47
References: Python公式リファレンス
48
References: Python公式リファレンス
49
References: PEP
50
References: blog等
51
References: CPython code
52
References: その他
53
ありがとうございました
Thank you po!�
Questions?
@shimizukawa
54