「and」と「or」は非ブール値でどのように機能しますか?


97

私はpythonを学ぼうとしていて、素晴らしくて短いが完全に意味のないコードに出くわしました

コンテキストは:

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

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

10 and 7-2

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

10 or 7 - 2

10を返します。

これは合法/信頼できるスタイルですか、それともこれについて何か問題がありますか?


1
and(およびor)は、ブール値の操作やブール値の返却に限定されません。
cs95 2017年

1
IMNSHO:これは、やや混乱する方法です。ブール値(明確な最小値と最大値がありますか)と数値(最小値と最大値の違いは何ですか)を返すことになっているのか、私にはわかりません。後者の場合、長さゼロのリストの差を数値として与えることが理にかなっているかどうかという問題もあります。(代わりNoneまたは例外)
ilkkachu 2017年

7
他の人が説明したように、それは機能し0ますが、1つの考えられる問題は、それが返された場合、args空であるか空ではないがすべての要素が等しいかどうかを判断できないことです。
特にライム

@EspeciallyLime:まさに。私はそれを私の答えで述べました。
Eric Duminil 2017年

回答:


135

TL; DR

まず、2つの論理演算子andとの2つの動作を要約しorます。これらのイディオムは、以下の議論の基礎を形成します。

and

ファルシーがある場合は最初の値を返し、そうでない場合は式の最後の値を返します。

or

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

動作もdocs、特に次の表にまとめられてます。

ここに画像の説明を入力してください

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


「真実性」および「真実性」の評価

ステートメント

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

ある非常に ニシキヘビ「と言った場合の方法簡潔な(と、おそらくあまり読み)args空にする、結果を返さないでmax(args) - min(args)、そうでない場合はリターンが」0。一般に、if-else式のより簡潔な表現です。例えば、

exp1 and exp2

(大まかに)変換する必要があります:

r1 = exp1
if r1:
    r1 = exp2

または、同等に、

r1 = exp1 if exp1 else exp2

同様に、

exp1 or exp2

同等です、

r1 = exp1
if not r1:
    r1 = exp2

どこexp1exp2任意のPythonオブジェクト、またはいくつかのオブジェクトを返す式です。ここでの論理演算子andor演算子の使用法を理解するための鍵は、それらが操作やブール値を返すことに制限されていないことを理解することです。真実値を持つすべてのオブジェクトをここでテストできます。これにはintstrlistdicttuplesetNoneType、およびユーザー定義オブジェクトを。短絡回路のルールも引き続き適用されます。

しかし、真実とは何ですか?
条件式で使用した場合のオブジェクトの評価方法を指します。@Patrick Haughはこの投稿で真実性をうまくまとめています。

次の「偽」を除いて、すべての値は「真実」と見なされます。

  • None
  • False
  • 0
  • 0.0
  • 0j
  • Decimal(0)
  • Fraction(0, 1)
  • [] - 空っぽ list
  • {} - 空っぽ dict
  • () - 空っぽ tuple
  • '' - 空っぽ str
  • b'' - 空っぽ bytes
  • set() - 空っぽ set
  • 空のrangeようなrange(0)
  • オブジェクト
    • obj.__bool__() 戻り値 False
    • obj.__len__() 戻り値 0

「真実の」値は、ifor while ステートメントによって実行されるチェックを満たします。「真実」と「虚偽」を使用して、boolTrueとを区別し ますFalse


どの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番目の式の結果であることに注意してください。最初の式がFalsyと評価された場合、返される結果は最初の式の結果です。

上記の関数では、Ifがfoo1つ以上の引数を受け取った場合、len(args)0(正の数)より大きいため、返される結果はになりmax(args) - min(args)ます。引数が渡されない場合OTOH、len(args)ある0Falsyである、と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)

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


どのor作品

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

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


33
簡潔にするためにすべての明快さを犠牲にすることは「パイソン的」ではありません。これは単純な構造ではありません。
DBedrenko 2017年

11
Pythonの条件式によって、この構文はあまり一般的ではなくなったことに注意してください。私は確かにmax(args)-len(args)の場合はmin(args)を、そうでない場合は0を優先します。
richardb 2017年

3
最初に混乱するもう1つの一般的な問題は、存在しない場合に値を割り当てることです: "some_var = arg or 3"
Erik

12
@Baldrickkが3項演算子を支持してこの構文のバッシングを始める前に、n項条件式に関しては、3項演算子がすぐに手に負えなくなることに注意してください。たとえば、if ... else (if ... else (if ... else (if ... else ...)))と同様に書き直すことができます。... and ... and ... and ... and ...その時点で、どちらの場合でも読みやすさを主張することは本当に難しくなります。
cs95 2017年

4
簡潔にするために明快さを犠牲にするのはpythonicではありませんが、これはそうではありません。それはよく知られた慣用句です。これは、他のイディオムと同様に、習得する必要があるイディオムですが、「明快さを犠牲にする」ことはほとんどありません。
Miles Rout

18

Pythonドキュメントからの引用

どちらもという注意andor 制限しない値を入力し、彼らはに戻ってFalseTrueではなく、返す最後に評価の引数を。これは、たとえば、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である式を返します。残りの式の評価は冗長です。

代わりにハードコアを返すのTrueFalse、Pythonの戻りTruthyまたはFalseyは、とにかくに評価しようとしていますTrueFalse。式をそのまま使用しても、引き続き機能します。


何を知るためにTruthyFalsey、チェックパトリック・ハウの答えを


7

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

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

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

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

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

したがって、コード

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

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


5

これは合法/信頼できるスタイルですか、それともこれについて何か問題がありますか?

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

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

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

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

真でない値が渡されalistた場合、デフォルトで空のリストにifなり、ステートメントと変更可能なデフォルト引数の問題を回避する便利な方法


3

ガチャ

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

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

まず、がfn返された場合0、パラメータなしで呼び出されたか、1つのパラメータで呼び出されたか、複数の等しいパラメータで呼び出されたかを確認できません。

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

どういうfn意味ですか?

そして、Pythonは動的言語です。何fnが何をするか、入力がどうあるべきか、出力がどのように見えるべきかはどこにも指定されていません。したがって、関数に正しく名前を付けることが非常に重要です。同様に、引数を呼び出す必要はありませんargsdelta(*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

0

これは合法/信頼できるスタイルですか、それともこれについて何か問題がありますか?

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

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

したがって、実際にそれを有利に使用できます。簡潔にするために、これは私がそれを見る方法です:

Or オペレーター

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

And オペレーター

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

舞台裏

Pythonでは、すべての数値がTrue0以外として解釈されます。

0 and 10 

と同じです:

False and True

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


0

はい。これはの正しい動作と比較です。

Pythonで少なくとも、A and B戻るB場合A、本質的にTrue場合を含むANOT NULLでないNoneNOT(例えば、空などの空の容器listdictなど)。AIFF Aは基本的にFalseor NoneまたはEmptyまたはNullを返します。

一方、A or B戻るA場合にA本質的True場合を含めA、NOT NULLではないNone(例えば、空のような空でない容器listdictなど)それ以外の場合は戻り、B

Pythonでは、non-null空ではないオブジェクトはTrueと評価されるため、ブール値のように扱われるため、この動作に気づかない(または見落とす)のは簡単です。

たとえば、次のすべては「True」を出力します

if [102]: 
    print "True"
else: 
    print "False"

if "anything that is not empty or None": 
    print "True"
else: 
    print "False"

if {1, 2, 3}: 
    print "True"
else: 
    print "False"

一方、以下はすべて「False」を出力します

if []: 
    print "True"
else: 
    print "False"

if "": 
    print "True"
else: 
    print "False"

if set ([]): 
    print "True"
else: 
    print "False"

ありがとうございました。A基本的に書きたかったTrueです。修正。
emmanuelsa 2017年

0

簡単に理解するには

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番目の値を返します。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.