web-dev-qa-db-ja.com

「a、b、cのいずれかであれば全部ではない」のPython構文

私はゼロまたは3つのコマンドライン引数を受け取ることができるpythonスクリプトを持っています。 (デフォルトの動作で実行されるか、または3つの値すべてを指定する必要があります。)

次のようなものに理想的な構文は何ですか。

if a and (not b or not c) or b and (not a or not c) or c and (not b or not a):

125
Chris Wilson

あなたが最小の形式を意味するならば、これと行きなさい:

if (not a or not b or not c) and (a or b or c):

これはあなたの質問のタイトルを翻訳します。

更新:VolatilityとSuprが正しく言っているように、De Morganの法則を適用して同等のものを得ることができます。

if (a or b or c) and not (a and b and c):

私にとってのアドバイスは、あなたにとっても他のプログラマーにとってもより重要な形式を使うことです。最初のものは"偽のものもあるが真のものもある"、2番目の"真のものはあるがすべてではない"を意味する。私がハードウェアでこれを最適化または実行するのであれば、2番目を選択します。ここでは最も読みやすいものを選択します(テストする条件とその名前も考慮に入れて)。私は最初を選びました。

226

どうですか?

conditions = [a, b, c]
if any(conditions) and not all(conditions):
   ...

その他の亜種

if 1 <= sum(map(bool, conditions)) <= 2:
   ...
233
defuz

この質問にはすでに多くの支持された回答と承認された回答がありましたが、これまでのところそれらはすべてブール問題を表現するためのさまざまな方法に気を取られ重要なポイントを逃しました。

私はゼロまたは3つのコマンドライン引数を受け取ることができるpythonスクリプトを持っています。 (デフォルトの動作で実行されるか、3つの値すべてを指定する必要があります)

このロジックはそもそもあなたのコードの責任であるべきではありません、むしろargparseモジュールによって扱われるべきです。複雑なif文を書くのを煩わさないでください。代わりに、引数パーサを次のように設定してください。

#!/usr/bin/env python
import argparse as ap
parser = ap.ArgumentParser()
parser.add_argument('--foo', nargs=3, default=['x', 'y', 'z'])
args = parser.parse_args()
print(args.foo)

そしてはい、それは位置引数ではなくオプションであるべきです。結局それはオプションであるからです。


編集:コメント内のLarsHの懸念に対処するために、以下がインターフェースを必要としていると確信した場合の記述例です。 3または0位置指定引数。 オプション引数はオプションであるべきだから、私は前のインターフェースがより良いスタイルであると思うのですが、これは別の方法です:完全のために。 usageは誤った使い方をするメッセージを自動的に生成するので、パーサーを作成するときは、上書きするkwarg argparseに注意してください。

#!/usr/bin/env python
import argparse as ap
parser = ap.ArgumentParser(usage='%(prog)s [-h] [a b c]\n')
parser.add_argument('abc', nargs='*', help='specify 3 or 0 items', default=['x', 'y', 'z'])
args = parser.parse_args()
if len(args.abc) != 3:
  parser.error('expected 3 arguments')
print(args.abc)

使用例をいくつか示します。

# default case
wim@wim-zenbook:/tmp$ ./three_or_none.py 
['x', 'y', 'z']

# explicit case
wim@wim-zenbook:/tmp$ ./three_or_none.py 1 2 3
['1', '2', '3']

# example failure mode
wim@wim-zenbook:/tmp$ ./three_or_none.py 1 2 
usage: three_or_none.py [-h] [a b c]
three_or_none.py: error: expected 3 arguments
113
wim

私は行きます:

conds = iter([a, b, c])
if any(conds) and not any(conds):
    # okay...

私はこれがかなり効率的に短絡するべきだと思います

説明

condsをイテレータにすることによって、anyを最初に使用するときには短絡して、項目がtrueの場合はイテレータは次の要素を指すようになります。そうでなければ、それはリスト全体を消費してFalseになります。次のanyはイテラブルの残りの項目を取り、他の真の値がないことを確認します。もしあれば、ステートメント全体が真となることはできません。再び回路)。最後のanyFalseを返すか、イテラブルを使い果たしてTrueになります。

注:上記は条件が1つだけ設定されているかどうかをチェックします


1つ以上の項目がすべての項目に設定されているわけではないかどうかを確認したい場合は、次のようにします。

not all(conds) and any(conds)
32
Jon Clements

英語の文:

「aまたはbまたはcだがすべてではない場合」

このロジックに変換されます。

(a or b or c) and not (a and b and c)

「しかし」という言葉は通常、接続詞、つまり「and」を意味します。さらに、「それらのすべて」は条件の接続詞に変換されます。この条件、andその条件、andその他の条件です。 "not"はその接続詞全体を逆にします。

私は受け入れられた答えに同意しません。作者は仕様に最も直接的な解釈を適用することを怠り、少数の演算子に表現を単純化するためにDe Morganの法則を適用することを怠った。

 not a or not b or not c  ->  not (a and b and c)

答えは「最小限の形」であると主張しながら。

22
Kaz

3つの条件のうち1つだけがTrueである場合、これはTrueを返します。おそらくあなたのサンプルコードに欲しいものがあります。

if sum(1 for x in (a,b,c) if x) == 1:
10
eumiro

どうですか?(固有の条件)

if (bool(a) + bool(b) + bool(c) == 1):

注意してください、あなたも2つの条件を許可するなら、あなたはそれをすることができます

if (bool(a) + bool(b) + bool(c) in [1,2]):

明確にするために、あなたはどのくらいのパラメータが論理的に真であるかに基づいてあなたの決定をしたいです(文字列引数の場合 - 空ではない)?

argsne = (1 if a else 0) + (1 if b else 0) + (1 if c else 0)

それからあなたは決断をしました:

if ( 0 < argsne < 3 ):
 doSth() 

今論理はより明確です。

6
Danubian Sailor

そしてなぜそれらを数えないのですか?

import sys
a = sys.argv
if len(a) = 1 :  
    # No arguments were given, the program name count as one
Elif len(a) = 4 :
    # Three arguments were given
else :
    # another amount of arguments was given
5
Louis

少し戸惑うことを気にしないのであれば、0 < (a + b + c) < 3を使って単純にロールバックできます。1〜2つのtrueステートメントがある場合はtrueを返し、すべてfalseまたはない場合はfalseを返します。

これは、変数を1回だけ評価するため、ブールを評価するために関数を使用する場合も単純化されます。つまり、関数をインラインで記述でき、変数を一時的に格納する必要がないということです。 (例:0 < ( a(x) + b(x) + c(x) ) < 3

5
Spinno

私がそれを理解しているように、あなたは3つの引数を受け取る関数を持っています、しかしそれがそうでなければそれはデフォルトの振舞いで動くでしょう。 1つか2つの引数が与えられたときに何が起こるべきか説明していないので、私はそれが単にデフォルトの振る舞いをするべきであると思います。その場合、私はあなたが以下の答えが非常に有利であると思うだろうと思います:

def method(a=None, b=None, c=None):
    if all([a, b, c]):
        # received 3 arguments
    else:
        # default behavior

ただし、1つまたは2つの引数を異なる方法で処理したい場合は、次のようにします。

def method(a=None, b=None, c=None):
    args = [a, b, c]
    if all(args):
        # received 3 arguments
    Elif not any(args):
        # default behavior
    else:
        # some args (raise exception?)

注:これは "False"の値がこのメソッドに渡されないことを前提としています。

4
Inbar Rose

質問には、3つの引数すべて(aとbとc)、またはいずれも必要ない(aまたはbとc)のいずれも必要ないと記載されています。

これは与える:

(aとbとc)かどうか(aかbかc)

4

もしあなたが条件のイテレータを使って作業していると、アクセスが遅くなるかもしれません。しかし、各要素に複数回アクセスする必要はなく、またすべての要素を読む必要もありません。これは無限の発電機で働く解決策です:

#!/usr/bin/env python3
from random import randint
from itertools import tee

def generate_random():
    while True:
        yield bool(randint(0,1))

def any_but_not_all2(s): # elegant
    t1, t2 = tee(s)
    return False in t1 and True in t2 # could also use "not all(...) and any(...)"

def any_but_not_all(s): # simple
    hadFalse = False
    hadTrue = False
    for i in s:
        if i:
            hadTrue = True
        else:
            hadFalse = True
        if hadTrue and hadFalse:
            return True
    return False


r1, r2 = tee(generate_random())
assert any_but_not_all(r1)
assert any_but_not_all2(r2)

assert not any_but_not_all([True, True])
assert not any_but_not_all2([True, True])

assert not any_but_not_all([])
assert not any_but_not_all2([])

assert any_but_not_all([True, False])
assert any_but_not_all2([True, False])
2
Janus Troelsen

指定されたすべてのboolTrueの場合、または指定されたすべてのboolFalse...の場合.
それらはすべて等しいものです。

そのため、異なるboolsに評価される2つの要素を見つける必要があります。
少なくとも1つのTrueと少なくとも1つのFalseがあることを知るため。

私の短い解決策:

not bool(a)==bool(b)==bool(c)

私はそれが短絡していると信じています、故にAFAIK a==b==ca==b and b==cと等しくなるようにします。

私の一般的な解決策:

def _any_but_not_all(first, iterable): #doing dirty work
    bool_first=bool(first)
    for x in iterable:
        if bool(x) is not bool_first:
            return True
    return False

def any_but_not_all(arg, *args): #takes any amount of args convertable to bool
    return _any_but_not_all(arg, args)

def v_any_but_not_all(iterable): #takes iterable or iterator
    iterator=iter(iterable)
    return _any_but_not_all(next(iterator), iterator)

複数のイテラブルを扱うコードも書いたが、無意味だと思うのでここから削除した。しかし、まだ利用可能です ここ

0
GingerPlusPlus