note.nkmk.me

Pythonの組み込み関数all(), any()の使い方

Date: 2018-10-20 / Modified: 2019-08-28 / tags: Python

Pythonでリストやタプルなどのイテラブルオブジェクトの要素がすべてTrue(真)か、いずれか一つでもTrueか、あるいは、すべてFalse(偽)かを判定するには組み込み関数all(), any()を使う。

ここでは、all()およびany()の使い方として、以下の内容について説明する。

  • Pythonにおける真偽値の判定
  • すべての要素がTrueか判定: all()
  • いずれかの要素がTrueか判定: any()
  • すべての要素がFalseか判定: not any()
  • 条件で判定: リスト内包表記、ジェネレータ式
    • リスト内包表記、ジェネレータ式に対するall(), any()
    • ジェネレータ式のメリット(処理速度比較)
  • 条件を満たす要素の数をカウント
スポンサーリンク

Pythonにおける真偽値の判定

Pythonには真偽値bool型のTrueFalseがあるが、if文などの条件での判定では、数値や文字列などの他の型もTrueFalseかいずれかに判定される。

以下のオブジェクトはFalseと判定される。

  • 偽であると定義されている定数: NoneFalse
  • 数値型におけるゼロ: 0, 0.0, 0j(複素数), Decimal(0), Fraction(0, 1)
  • 空のシーケンスまたはコレクション: '', (), [], {}, set(), range(0)

その他のオブジェクトはすべてTrueと判定される。

詳細は以下の記事を参照。

以降で説明するall(), any()においてもこのルールに従って真偽が判定される。

すべての要素がTrueか判定: all()

all()は引数に指定したイテラブルオブジェクトの要素がすべてTrueと判定されるとTrueを返す。一つでもFalseがあればFalse

print(all([True, True, True]))
# True

print(all([True, False, True]))
# False

リストに限らず、タプルや集合set型も引数に指定可。

print(all((True, True, True)))
# True

print(all({True, True, True}))
# True

all()は以下のコードと等価。

def all(iterable):
    for element in iterable:
        if not element:
            return False
    return True

したがって、空のイテラブルオブジェクトにはTrueを返す。

print(all([]))
# True

上述のように、bool型のTrue, Falseだけでなく、その他の型も判定して結果が返される。

print(all([100, [0, 1, 2], 'abc']))
# True

print(all([100, [0, 1, 2], 'abc', {}]))
# False

いずれかの要素がTrueか判定: any()

any()は引数に指定したイテラブルオブジェクトの要素のいずれかがTrueと判定されるとTrueを返す。すべてFalseであればFalse

print(any([True, False, False]))
# True

print(any([False, False, False]))
# False

リストに限らず、タプルや集合set型も引数に指定可。

print(any((True, False, False)))
# True

print(any({True, False, False}))
# True

any()は以下のコードと等価。

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False

したがって、空のイテラブルオブジェクトにはFalseを返す。

print(any([]))
# False

上述のように、bool型のTrue, Falseだけでなく、その他の型も判定して結果が返される。

print(any([False, None, 0, 0.0, 0 + 0j, '', [], {}, ()]))
# False

print(any([False, None, 0, 0.0, 0 + 0j, '', [], {}, (), 1]))
# True

すべての要素がFalseか判定: not any()

any()はひとつでもTrueがあるとTrueを返し、すべての要素がFalseのときのみFalseを返す。

したがって、notany()の否定をとることで、すべての要素がFalseのときのみTrueとすることができる。

print(not any([False, False, False]))
# True

print(not any([True, False, False]))
# False

条件で判定: リスト内包表記、ジェネレータ式

リスト内包表記、ジェネレータ式に対するall(), any()

これまでの例はイテラブルオブジェクトの要素をそのままTrueFalseか判定していたが、内包表記を使うと、任意の条件に対してall()any()を適用できる。

すべての要素が条件を満たすかどうかなどの判定が可能。

あるイテラブルオブジェクトの各要素に対する条件判定の結果は、リスト内包表記を使って以下のように取得できる。

l = [0, 1, 2, 3, 4]

print([i > 2 for i in l])
# [False, False, False, True, True]

リスト内包表記の詳細は以下の記事を参照。

この結果をall(), any()の引数に指定すると、各要素がすべて条件を満たすか、ひとつでも条件を満たすかといった判定ができる。

print(all([i > 2 for i in l]))
# False

print(any([i > 2 for i in l]))
# True

ここで、リスト内包表記の[]()に変えるとジェネレータ式となり、リストではなくジェネレータを返す。

print(type([i > 2 for i in l]))
# <class 'list'>

print(type((i > 2 for i in l)))
# <class 'generator'>

ジェネレータ式を唯一の引数として関数を呼び出す場合、()を省略可能。

print(type(i > 2 for i in l))
# <class 'generator'>

これをall(), any()の引数に指定できる。

print(all(i > 2 for i in l))
# False

print(any(i > 2 for i in l))
# True

ジェネレータ式のメリット(処理速度比較)

ジェネレータはリストと異なり逐次処理されるので、処理時間やメモリ使用量を抑えられるというメリットがある。

以下のような要素数100000個の連番のリストを例とする。

l = list(range(100000))
print(l[:5])
# [0, 1, 2, 3, 4]

print(l[-5:])
# [99995, 99996, 99997, 99998, 99999]

print(len(l))
# 100000

このリストに対するリスト内包表記とジェネレータ式のall(), any()の処理時間を比較する。以下の例はJupyter Notebookのマジックコマンド%%timeitを利用しており、Pythonスクリプトとして実行しても計測されないので注意。

all()の場合、Falseが一つでもあると結果がFalseだと確定する。

リスト内包表記ではすべての要素に対して式(下の例の場合はi < 0)を評価してリストを生成してall()any()に渡すが、ジェネレータ式では先頭の要素から順次処理される。Falseの要素がある時点でall()の結果が確定するため、例えば先頭の要素がFalseである場合、ジェネレータ式のほうが遥かに速くなる。

%%timeit
all([i < 0 for i in l])
# 4.15 ms ± 117 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit
all(i < 0 for i in l)
# 469 ns ± 6.12 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

すべての要素がTrueall()の結果がTrue)だと最後の要素まで処理する必要があるのでジェネレータ式でも速くならない。例の場合はむしろ遅くなっている。

%%timeit
all([i >= 0 for i in l])
# 4.5 ms ± 57.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit
all(i >= 0 for i in l)
# 5.49 ms ± 255 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

このように、ジェネレータ式はどこでFalseと判定されるかによって処理時間が異なる。中央でFalseだと判定される場合は半分くらいの処理時間になる。

%%timeit
all(i < 50000 for i in l)
# 2.73 ms ± 37.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

any()でも同様。any()の場合、Trueが一つでもあると結果がTrueだと確定するため、ジェネレータ式ではTrueが出てきた時点で処理が終了する。

%%timeit
any([i >= 0 for i in l])
# 4.2 ms ± 183 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit
any(i >= 0 for i in l)
# 468 ns ± 4.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%%timeit
any([i < 0 for i in l])
# 4.56 ms ± 180 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit
any(i < 0 for i in l)
# 5.33 ms ± 45.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit
any(i > 50000 for i in l)
# 2.78 ms ± 120 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

条件を満たす要素の数をカウント

True1False0として処理されるので、sum()を使うとTrueの数、すなわち、条件を満たす要素の数を取得できる。

print(sum(i > 2 for i in l))
# 2

Falseの数をカウントしたい場合はnotを使えばOK。

print(sum(not (i > 2) for i in l))
# 3
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事