「and」と「or」は非ブール値ではどのように動作しますか? 質問する

「and」と「or」は非ブール値ではどのように動作しますか? 質問する

私はPythonを学ぼうとしているのですが、短くて良いけれど全く意味をなさないコードに出会いました。

文脈は次のとおりです。

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

何をしているのかは分かりますが、なぜ Python はこれを実行するのでしょうか (つまり、True/False ではなく値を返すのでしょうか)?

10 and 7-2

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

10 or 7 - 2

10 を返します。

これは正当/信頼できるスタイルですか、それとも何か落とし穴がありますか?

ベストアンサー1

要約

まず、2 つの論理演算子との動作をまとめますandorこれらの慣用句が、以下の説明の基礎となります。

and

式に Falsy 値がある場合は最初の値を返し、ない場合は最後の値を返します。

or

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

この行動は次のように要約される。ドキュメント特にこの表では:

手術 結果
x or y もしバツ偽であればええ、 それ以外バツ
x and y もしバツ偽であればバツ、 それ以外ええ
not x もしバツが偽の場合、そうでTrueなければFalse

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


"Truthiness", and "Truthy" Evaluations

The statement

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

Is a very pythonic concise (and arguably less readable) way of saying "if args is not empty, return the result of max(args) - min(args)", otherwise return 0. In general, it is a more concise representation of an if-else expression. For example,

exp1 and exp2

Should (roughly) translate to:

r1 = exp1
if r1:
    r1 = exp2

Or, equivalently,

r1 = exp2 if exp1 else exp1

Similarly,

exp1 or exp2

Should (roughly) translate to:

r1 = exp1
if not r1:
    r1 = exp2

Or, equivalently,

r1 = exp1 if exp1 else exp2

Where exp1 and exp2 are arbitrary python objects, or expressions that return some object. The key to understanding the uses of the logical and and or operators here is understanding that they are not restricted to operating on, or returning boolean values. Any object with a truthiness value can be tested here. This includes int, str, list, dict, tuple, set, NoneType, and user defined objects. Short circuiting rules still apply as well.

But what is truthiness?
It refers to how objects are evaluated when used in conditional expressions. @Patrick Haugh summarises truthiness nicely in this post.

All values are considered "truthy" except for the following, which are "falsy":

  • None
  • False
  • 0
  • 0.0
  • 0j
  • Decimal(0)
  • Fraction(0, 1)
  • [] - an empty list
  • {} - an empty dict
  • () - an empty tuple
  • '' - an empty str
  • b'' - an empty bytes
  • set() - an empty set
  • an empty range, like range(0)
  • objects for which
    • obj.__bool__() returns False
    • obj.__len__() returns 0

A "truthy" value will satisfy the check performed by if or while statements. We use "truthy" and "falsy" to differentiate from the bool values True and False.


How and Works

We build on OP's question as a segue into a discussion on how these operators in these instances.

Given a function with the definition

def foo(*args):
    ...

How do I return the difference between the minimum and maximum value in a list of zero or more arguments?

Finding the minimum and maximum is easy (use the inbuilt functions!). The only snag here is appropriately handling the corner case where the argument list could be empty (for example, calling foo()). We can do both in a single line thanks to the and operator:

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

foo()
# 0

Since and is used, the second expression must also be evaluated if the first is True. Note that, if the first expression is evaluated to be truthy, the return value is always the result of the second expression. If the first expression is evaluated to be Falsy, then the result returned is the result of the first expression.

In the function above, If foo receives one or more arguments, len(args) is greater than 0 (a positive number), so the result returned is max(args) - min(args). OTOH, if no arguments are passed, len(args) is 0 which is Falsy, and 0 is returned.

Note that an alternative way to write this function would be:

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

Or, more concisely,

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

If course, none of these functions perform any type checking, so unless you completely trust the input provided, do not rely on the simplicity of these constructs.


How or Works

or同様の方法で、架空の例を使っての仕組みを説明します。

定義が次の関数であるとする

def foo(*args):
    ...

foo以上の数字をすべて返すにはどうしたらよいでしょうか9000?

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 の結果は であり、[]これは Falsy です。そのため、2 番目の式が評価され (空でない文字列)、 が返されます。

条件文を使うと、この関数は次のように書き直すことができます。

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

以前と同様に、この構造はエラー処理の点でより柔軟です。

おすすめ記事