3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

PEP 8 による暗黙の仕様

Last updated at Posted at 2022-03-08

シーケンスの長さを判定するケース

list,tuple,str などのシーケンスの場合、長さ 0 の判定では

if seq:      # PEP 8 - OK
if len(seq): # PEP 8 - NG

とされているようです。

この記述がトラブルとなるケース

独自クラスやモジュールでシーケンスに対応しつつ

  中身が特定パターンのときに __bool__() が True (長さが 0 でなくても False)を返す

オブジェクトを作ると不具合の原因になりかねません。

どう呼び出されるかのテスト(シーケンス非対応だが)
class A:
    def __init__(self): pass

    def __bool__(self):
        print('A.__bool__')
        return False

    def __len__(self):
        print('A.__len__')
        return 1


class B:
    def __init__(self): pass

    def __len__(self):
        print('B.__len__')
        return 1

a = A()
if a:
    print('if a >> TRUE')
else:
    print('if a >> FALSE')
if len(a):
    print('if len(a) >> TRUE')
else:
    print('if len(a) >> FALSE')

b = B()
if b:
    print('if b >> TRUE')
else:
    print('if b >> FALSE')
if len(b):
    print('if len(b) >> TRUE')
else:
    print('if len(b) >> FALSE')
print('bool(b) >>', bool(b))
ターミナルでの実行結果
$ python3 sample.py
A.__bool__
if a >> FALSE
A.__len__
if len(a) >> TRUE
B.__len__
if b >> TRUE
B.__len__
if len(b) >> TRUE
B.__len__
bool(b) >> True
$
$ python3
>>> l = list()
>>> l.__bool__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__bool__'
>>> l.__len__()
0
>>> bool(l)
False
>>> s = str()
>>> s.__bool__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute '__bool__'
>>> s.__len__()
0
>>> bool(s)
False
>>> t = tuple()
>>> t.__bool__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute '__bool__'
>>> t.__len__()
0
>>> bool(t)
False

if 文では __len__() より __bool__() が優先されることが分かる。

list, str, tuple は __bool__() を持っていないらしい。

PEP 8 に従うならば

シーケンスを扱う PEP 8 準拠のプログラムに、独自のオブジェクトを渡すのならば

    def __bool__(self):
        return self.__len__() != 0

長さを調べて 0 かどうか(上記相当)以外の処理を __bool__() メソッドで作るのはやめたほうがいいことになります。

世の中のプログラムが PEP 8 準拠ばかりならば、事実上の仕様と考えた方がよさそうです。

シーケンスに対応しつつ、シーケンスの中身で __bool__() の戻り値を決めるオブジェクトを作った場合、それをシーケンス処理が出来る他所のプログラムに渡すには、長さが 0 のときの判定方法を確認する必要がありそうです。__bool__() で判定していないかを…


この地雷は以前踏んだので、シーケンスの長さで判定する処理は必ず len を書くようにしている。

3
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?