web-dev-qa-db-ja.com

「and」/「or」演算子の奇妙な使用

私はpythonを学ぼうとしていますが、ニースで短いコードに出くわしましたが、まったく意味がありません

コンテキストは:

def fn(*args):
    return len(args) and max(args)-min(args)

私はそれが何をしているのかわかりますが、なぜpythonはこれを行うのですか-つまり、True/Falseではなく値を返しますか?

10 and 7-2

5を返します。同様に、とを変更すると、機能が変更されます。そう

10 or 7 - 2

10を返します。

これは合法/信頼できるスタイルですか、またはこれに関する落とし穴はありますか?

80
Marcin

TL; DR

まず、2つの論理演算子andおよびorの2つの動作を要約します。これらのイディオムは、以下の説明の基礎となります。

and

最初のFalsy値があればそれを返し、そうでなければ式の最後の値を返します。

or

最初のTruthy値がある場合はそれを返し、そうでない場合は式の最後の値を返します。

動作も ドキュメント にまとめられています。特にこの表では:

enter image description here

オペランドに関係なくブール値を返す唯一の演算子はnot演算子です。


「真実」と「真実」の評価

声明

len(args) and max(args) - min(args)

very パイソン 「argsが空でない場合、max(args) - min(args)の結果を返す」という簡潔な(そしておそらく間違いなく読みにくい)方法、そうでなければ0を返します。一般的に、それはif-else式のより簡潔な表現です。例えば、

exp1 and exp2

(大まかに)次のように変換する必要があります。

r1 = exp1
if not r1:
    r1 = exp2

または、同等に、

r1 = exp1 if exp1 else exp2

exp1およびexp2は、任意のpythonオブジェクト、またはオブジェクトを返す式です。ここでの論理andおよびor演算子の使用を理解するための鍵は、それらが演算またはブール値を返すことに制限されていないことを理解することです。ここでは、真偽値を持つオブジェクトをテストできます。これには、intstrlistdictTuplesetNoneType、およびユーザー定義オブジェクトが含まれます。短絡ルールも適用されます。

しかし、真実とは何ですか?
条件式で使用した場合にオブジェクトがどのように評価されるかを示します。 @Patrick Haughは、真実を この投稿 でうまくまとめています。

「偽」である以下を除き、すべての値は「真」と見なされます。

  • None
  • False
  • 0
  • 0.0
  • 0j
  • Decimal(0)
  • Fraction(0, 1)
  • []-空のlist
  • {}-空のdict
  • ()-空のTuple
  • ''-空のstr
  • b''-空のbytes
  • set()-空のset
  • range(0)のような空のrange
  • の対象
    • obj.__bool__()Falseを返します
    • obj.__len__()0を返します

「真の」値は、ifまたはwhileステートメントによって実行されるチェックを満たします。 boolの値TrueおよびFalseと区別するために、「truthy」および「falsy」を使用します。


andの仕組み

私たちは、OPの質問に基づいて、これらのインスタンスでこれらの演算子がどのように機能するかについての議論のセグエとして構築します。

定義を持つ関数を与えられた

def foo(*args):
    ...

ゼロ個以上の引数のリストで最小値と最大値の差を返すにはどうすればよいですか?

最小値と最大値を見つけるのは簡単です(組み込み関数を使用してください!)。ここでの唯一の障害は、引数リストが空になる可能性があるコーナーケースを適切に処理することです(たとえば、foo()の呼び出し)。 and演算子のおかげで、両方を1行で実行できます。

def foo(*args):
     return len(args) and max(args) - min(args)
foo(1, 2, 3, 4, 5)
# 4

foo()
# 0

andが使用されるため、最初の式がTrueの場合、2番目の式も評価する必要があります。最初の式が真であると評価された場合、戻り値はalways2番目の式の結果であることに注意してください。最初の式が偽と評価された場合、返される結果は最初の式の結果です。

上記の関数で、fooが1つ以上の引数を受け取る場合、len(args)0(正の数)よりも大きいため、返される結果はmax(args) - min(args)です。 OTOH、引数が渡されない場合、len(args)0(Falsy)であり、0が返されます。

この関数を記述する別の方法は次のとおりです。

def foo(*args):
    if not len(args):
        return 0

    return max(args) - min(args)

または、より簡潔に、

def foo(*args):
    return 0 if not args else max(args) - min(args)

もちろん、これらの関数はいずれも型チェックを実行しないため、提供された入力を完全に信頼しない限り、do notはこれらの構造の単純さに依存します。


orの仕組み

orの動作を、不自然な例を使って同様に説明します。

定義を持つ関数を与えられた

def foo(*args):
    ...

9000を介してすべての数値を返すには、fooをどのように完成させますか?

ここでは、orを使用してコーナーケースを処理します。 fooを次のように定義します。

def foo(*args):
     return [x for x in args if x > 9000] or 'No number over 9000!'

foo(9004, 1, 2, 500)
# [9004]

foo(1, 2, 3, 4)
# 'No number over 9000!'

fooは、リストのフィルタリングを実行して、9000上のすべての数値を保持します。そのような番号が存在する場合、リストの内包表記の結果は空ではないリストであり、Truthyであるため、返されます(ここでは動作中の短絡)。そのような番号が存在しない場合、リストcompの結果は[]であり、これは偽です。したがって、2番目の式が評価され(空ではない文字列)、返されます。

条件を使用して、この関数を次のように書き換えることができます。

def foo(*args):
    r = [x for x in args if x > 9000]
    if not r:
        return 'No number over 9000!' 

    return r

前と同じように、この構造はエラー処理に関してより柔軟です。

112
cs95

Python Docs から引用

andorrestrictvalueおよびtypeFalseおよびTrueに戻りますが、代わりに最後に評価された引数。これは時々便利です。たとえば、sが空の場合にデフォルト値に置き換える必要がある文字列である場合、式s or 'foo'は目的の値を生成します。

したがって、これがPythonがブール式を評価するために設計された方法であり、上記のドキュメントはなぜそうしたのかについての洞察を与えてくれます。

ブール値を取得するには、型キャストするだけです。

return bool(len(args) and max(args)-min(args))

どうして?

短絡。

例えば:

2 and 3 # Returns 3 because 2 is Truthy so it has to check 3 too
0 and 3 # Returns 0 because 0 is Falsey and there's no need to check 3 at all

同じことがorにも当てはまります。つまり、検出されるとすぐにTruthyの式を返します。残りの式の評価は冗長です。 。

ハードコアTrueまたはFalseを返す代わりに、PythonはTruthyまたはFalsey、とにかくTrueまたはFalseに評価されます。式をそのまま使用できますが、それでも機能します。


TruthyおよびFalseyを確認するには、 Patrick Haughの答え

14
Amit Joki

andおよびorはブール論理を実行しますが、比較すると実際の値の1つを返します。 andを使用すると、値は左から右にブールコンテキストで評価されます。 、 ''、[]、()、{}、およびNoneはブールコンテキストではfalseです。それ以外はすべて真実です。

ブールコンテキストですべての値がtrueの場合、andは最後の値を返します。

>>> 2 and 5
5
>>> 2 and 5 and 10
10

ブール値のコンテキストでいずれかの値がfalseの場合andは、最初のfalse値を返します。

>>> '' and 5
''
>>> 2 and 0 and 5
0

だからコード

return len(args) and max(args)-min(args)

argsがある場合はmax(args)-min(args)の値を返します。それ以外の場合は、len(args)を返します(0)。

7
Nithin Varghese

これは合法/信頼できるスタイルですか、またはこれに関する落とし穴はありますか?

これは正当であり、最後の値が返される 短絡評価 です。

良い例を提供します。引数が渡されない場合、関数は0を返します。また、コードは引数が渡​​されない特別なケースをチェックする必要はありません。

これを使用する別の方法は、空のリストのような可変プリミティブへのデフォルトのNone引数です:

def fn(alist=None):
    alist = alist or []
    ....

真ではない値がalistに渡されると、デフォルトで空のリストになります。これは、ifステートメントと mutable default argument pitfall を避ける便利な方法です

5
salparadise

落とし穴

はい、いくつかの落とし穴があります。

fn() == fn(3) == fn(4, 4)

まず、fn0を返す場合、パラメーターなしで呼び出されたか、1つのパラメーターで呼び出されたか、複数の等しいパラメーターで呼び出されたかはわかりません。

>>> fn()
0
>>> fn(3)
0
>>> fn(3, 3, 3)
0

fnはどういう意味ですか?

そして、Pythonは動的言語です。 fnが何をするのか、その入力はどうあるべきか、出力はどのように見えるべきかはどこにも指定されていません。したがって、関数に正しく名前を付けることが本当に重要です。同様に、引数はargsと呼ばれる必要はありません。 delta(*numbers)またはcalculate_range(*numbers)は、関数が何をすべきかをよりよく説明する場合があります。

引数エラー

最後に、論理and演算子は、引数なしで呼び出された場合に関数が失敗するのを防ぐことになっています。ただし、一部の引数が数値ではない場合でも失敗します。

>>> fn('1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
>>> fn(1, '2')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: '>' not supported between instances of 'str' and 'int'
>>> fn('a', 'b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'

可能な代替案

「許可よりも許しを求める方が簡単」という原則 に従って関数を記述する方法を次に示します。

def delta(*numbers):
    try:
        return max(numbers) - min(numbers)
    except TypeError:
        raise ValueError("delta should only be called with numerical arguments") from None
    except ValueError:
        raise ValueError("delta should be called with at least one numerical argument") from None

例として:

>>> delta()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in delta
ValueError: delta should be called with at least one numerical argument
>>> delta(3)
0
>>> delta('a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 'b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta(3, 4.5)
1.5
>>> delta(3, 5, 7, 2)
5

deltaが引数なしで呼び出されたときに例外を発生させたくない場合は、それ以外では不可能な値を返すことができます(例:-1またはNone):

>>> def delta(*numbers):
...     try:
...         return max(numbers) - min(numbers)
...     except TypeError:
...         raise ValueError("delta should only be called with numerical arguments") from None
...     except ValueError:
...         return -1 # or None
... 
>>> 
>>> delta()
-1
3
Eric Duminil

これは合法/信頼できるスタイルですか、またはこれに関する落とし穴はありますか?

この質問に、合法で信頼できるだけでなく、極めて実用的でもあることを付け加えたいと思います。以下に簡単な例を示します。

>>>example_list = []
>>>print example_list or 'empty list'
empty list

したがって、あなたは本当にあなたの利点でそれを使うことができます。簡潔にするために、これは私がそれを見る方法です:

Or演算子

Pythonのor演算子は、最初のTruth-y値または最後の値を返し、停止します

And演算子

Pythonのand演算子は、最初のFalse-y値または最後の値を返し、停止します

舞台裏

Pythonでは、0を除くすべての数値はTrueとして解釈されます。したがって、次のように言います。

0 and 10 

次と同じです:

False and True

明らかにFalseです。したがって、0を返すことは論理的です

0
scharette

簡単な方法で理解するには、

AND:if first_val is False return first_val else second_value

例えば:

1 and 2 # here it will return 2 because 1 is not False

しかし、

0 and 2 # will return 0 because first value is 0 i.e False

および=>誰かが偽の場合、それは偽になります。両方が真の場合、それだけが真になります

または:if first_val is False return second_val else first_value

理由は、firstがfalseの場合、2がtrueかどうかをチェックするからです。

例えば:

1 or 2 # here it will return 1 because 1 is not False

しかし、

0 or 2 # will return 2 because first value is 0 i.e False

または=>誰かが偽の場合、真になります。したがって、2つの値が何であろうと最初の値がfalseの場合。そのため、可能な限り2番目の値を返します。

誰かが真実なら、それは真実になります。両方が偽の場合、偽になります。