89

Python には、真の値 (つまり、 ) を1 つだけ持つリストがありbool(value) is Trueます。これを確認する賢い方法はありますか?現在、リスト全体を反復処理して手動でチェックしています。

def only1(l)
    true_found = False
    for v in l:
        if v and not true_found:
            true_found=True
        elif v and true_found:
             return False #"Too Many Trues"
    return true_found

これはエレガントではなく、あまりpythonicではないようです。これを行うより賢い方法はありますか?

4

17 に答える 17

52

値を探しているだけなのか、論理的Trueに評価される他の値(または など) も探しているのかによって異なります。前者の場合:True11"hello"

def only1(l):
    return l.count(True) == 1

後者の場合:

def only1(l):
    return sum(bool(e) for e in l) == 1

これは、新しいリストを作成する必要なく、1 回の反復でカウントと変換の両方を行うためです。

于 2013-05-28T20:54:50.847 に答える
23

短絡動作を保持する 1 行の回答:

from itertools import ifilter, islice

def only1(l):
    return len(list(islice(ifilter(None, l), 2))) == 1

これは、比較的早い段階で 2 つ以上の真の値を持つ非常に大きな iterable の場合、他の代替手段よりも大幅に高速になります。

ifilter(None, itr)truey 要素のみを生成する iterable を返します ( xis truey if bool(x)returns True)。 islice(itr, 2)の最初の 2 つの要素のみを生成する iterable を返しitrます。これをリストに変換し、長さが 1 であることを確認することで、真の要素が 1 つだけ存在することを確認できます。2 つ見つかった後に追加の要素を確認する必要はありません。

タイミングの比較は次のとおりです。

  • セットアップ コード:

    In [1]: from itertools import islice, ifilter
    
    In [2]: def fj(l): return len(list(islice(ifilter(None, l), 2))) == 1
    
    In [3]: def david(l): return sum(bool(e) for e in l) == 1
    
  • 短絡動作を示す:

    In [4]: l = range(1000000)
    
    In [5]: %timeit fj(l)
    1000000 loops, best of 3: 1.77 us per loop
    
    In [6]: %timeit david(l)
    1 loops, best of 3: 194 ms per loop
    
  • 短絡が発生しない大きなリスト:

    In [7]: l = [0] * 1000000
    
    In [8]: %timeit fj(l)
    100 loops, best of 3: 10.2 ms per loop
    
    In [9]: %timeit david(l)
    1 loops, best of 3: 189 ms per loop
    
  • 小さなリスト:

    In [10]: l = [0]
    
    In [11]: %timeit fj(l)
    1000000 loops, best of 3: 1.77 us per loop
    
    In [12]: %timeit david(l)
    1000000 loops, best of 3: 990 ns per loop
    

したがって、このsum()アプローチは非常に小さなリストの方が高速ですが、入力リストが大きくなると、短絡が不可能な場合でも私のバージョンの方が高速になります。大きな入力で短絡が可能な場合、性能の違いは明らかです。

于 2013-05-28T21:00:15.537 に答える
15

私はネクロマンサーのバッジを獲得したかったので、Jon Clements の優れた回答を一般化して、短絡ロジックと高速な述語チェックの利点を維持しました。

したがって、次のとおりです。

N(真) = n

def n_trues(iterable, n=1):
    i = iter(iterable)
    return all(any(i) for j in range(n)) and not any(i)

N(真) <= n:

def up_to_n_trues(iterable, n=1):
    i = iter(iterable)
    all(any(i) for j in range(n))
    return not any(i)

N(真) >= n:

def at_least_n_trues(iterable, n=1):
    i = iter(iterable)
    return all(any(i) for j in range(n))

m <= N(真) <= n

def m_to_n_trues(iterable, m=1, n=1):
    i = iter(iterable)
    assert m <= n
    return at_least_n_trues(i, m) and up_to_n_trues(i, n - m)
于 2014-07-28T15:57:44.317 に答える
12
>>> l = [0, 0, 1, 0, 0]
>>> has_one_true = len([ d for d in l if d ]) == 1
>>> has_one_true
True
于 2013-05-28T20:58:39.687 に答える
5
if sum([bool(x) for x in list]) == 1

(すべての値がブール値であると仮定します。)

これはおそらく合計するだけでより高速になります

sum(list) == 1   

ただし、リストのデータ型によっては問題が発生する場合があります。

于 2013-05-28T20:55:18.503 に答える
5

できるよ:

x = [bool(i) for i in x]
return x.count(True) == 1

または

x = map(bool, x)
return x.count(True) == 1

@JoranBeasleyの方法に基づいて構築:

sum(map(bool, x)) == 1
于 2013-05-28T20:55:19.713 に答える
4

が 1 つしかない場合、 s のTrue長さは1 にする必要があります。True

def only_1(l): return 1 == len(filter(None, l))
于 2013-05-28T21:30:54.197 に答える
4

これは機能しているようで、 s だけでなく、あらゆる iterable を処理できるはずlistです。効率を最大化するために可能な限り短絡します。Python 2 と 3 の両方で動作します。

def only1(iterable):
    for i, x in enumerate(iterable):  # check each item in iterable
        if x: break                   # truthy value found
    else:
        return False                  # no truthy value found
    for x in iterable[i+1:]:          # one was found, see if there are any more
        if x: return False            #   found another...
    return True                       # only a single truthy value found

testcases = [  # [[iterable, expected result], ... ]
    [[                          ], False],
    [[False, False, False, False], False],
    [[True,  False, False, False], True],
    [[False, True,  False, False], True],
    [[False, False, False, True],  True],
    [[True,  False, True,  False], False],
    [[True,  True,  True,  True],  False],
]

for i, testcase in enumerate(testcases):
    correct = only1(testcase[0]) == testcase[1]
    print('only1(testcase[{}]): {}{}'.format(i, only1(testcase[0]),
                                             '' if correct else
                                             ', error given '+str(testcase[0])))

出力:

only1(testcase[0]): False
only1(testcase[1]): False
only1(testcase[2]): True
only1(testcase[3]): True
only1(testcase[4]): True
only1(testcase[5]): False
only1(testcase[6]): False
于 2013-05-29T02:00:30.507 に答える
3

最大で N 個の True 値に対して拡張された @JonClements` ソリューション:

# Extend any() to n true values
def _NTrue(i, n=1):
    for x in xrange(n):
        if any(i): # False for empty
            continue
        else:
            return False
    return True

def NTrue(iterable, n=1):
    i = iter(iterable)
    return any(i) and not _NTrue(i, n)

編集:より良いバージョン

def test(iterable, n=1): 
    i = iter(iterable) 
    return sum(any(i) for x in xrange(n+1)) <= n 

edit2:少なくとも m 個のTrueと最大 n 個の True を含めます

def test(iterable, n=1, m=1): 
    i = iter(iterable) 
    return  m <= sum(any(i) for x in xrange(n+1)) <= n
于 2013-05-28T22:05:38.990 に答える
2
def only1(l)
    sum(map(lambda x: 1 if x else 0, l)) == 1

説明: このmap関数は、 と を実行して、リストを別のリストにマップしTrue => 1ますFalse => 0。これで、True または False の代わりに 0 と 1 のリストができました。ここで、このリストを単純に合計し、それが 1 の場合、True 値は 1 つしかありませんでした。

于 2013-06-03T04:57:50.127 に答える
0

ショートサーキットはありませんが、真実であれば何でも機能するはずです。相互に排他的な議論を禁止する明確な方法を探しているときに、私はそれを見つけました:

if sum(1 for item in somelist if item) != 1:
    raise ValueError("or whatever...")
于 2016-08-07T00:08:26.873 に答える
0
import collections

def only_n(l, testval=True, n=1):
    counts = collections.Counter(l)
    return counts[testval] == n

線形時間。組み込みの Counter クラスを使用します。これは、カウントをチェックするために使用する必要があるものです。

質問を読み直すと、1 つの値ではなく、1 つの真の値しかないことを実際に確認したいようですTrue。これを試して:

import collections

def only_n(l, testval=True, coerce=bool, n=1):
    counts = collections.Counter((coerce(x) for x in l))
    return counts[testval] == n

最良の場合のパフォーマンスを向上させることはできますが、最悪の場合のパフォーマンスを向上させるものはありません。これも短くて読みやすい。

最高のパフォーマンスを実現するために最適化されたバージョンを次に示します。

import collections
import itertools

def only_n(l, testval=True, coerce=bool, n=1):
    counts = collections.Counter()
    def iterate_and_count():
        for x in itertools.imap(coerce,l):
            yield x
            if x == testval and counts[testval] > n:
               break
    counts.update(iterate_and_count())
    return counts[testval] == n

最悪の場合のパフォーマンスはk( のようにO(kn+c)) 高くなりますが、これは完全に一般的なものです。

パフォーマンスを試すためのアイデアは次のとおりです: http://ideone.com/ZRrv2m

于 2013-06-07T18:40:47.547 に答える
0

どうですか:

len([v for v in l if type(v) == bool and v])

ブール値の True 値のみをカウントする場合。

于 2017-11-03T11:06:50.420 に答える