リストとNumPy配列のブール演算とビット演算の動作の違いを説明するものは何ですか?
Pythonでの&
対and
の適切な使用について混乱しています。次の例で説明します。
mylist1 = [True, True, True, False, True]
mylist2 = [False, True, False, True, False]
>>> len(mylist1) == len(mylist2)
True
# ---- Example 1 ----
>>> mylist1 and mylist2
[False, True, False, True, False]
# I would have expected [False, True, False, False, False]
# ---- Example 2 ----
>>> mylist1 & mylist2
TypeError: unsupported operand type(s) for &: 'list' and 'list'
# Why not just like example 1?
>>> import numpy as np
# ---- Example 3 ----
>>> np.array(mylist1) and np.array(mylist2)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
# Why not just like Example 4?
# ---- Example 4 ----
>>> np.array(mylist1) & np.array(mylist2)
array([False, True, False, False, False], dtype=bool)
# This is the output I was expecting!
この回答 と この回答 は、 and
がブール演算であることを理解するのに役立ちましたただし、 &
はビット単位の操作です。
概念をよりよく理解するために bitwise操作 を読みましたが、上記の4つの例を理解するためにその情報を使用するのに苦労しています。
例4で目的の出力が得られたので問題ありませんが、and
と&
をいつ、どのように、なぜ使用する必要があるかについてはまだ混乱しています。リストとNumPy配列がこれらの演算子で異なる動作をするのはなぜですか?
リストとNumPy配列を異なる方法で処理する理由を説明するために、ブール演算とビット演算の違いを理解するのに役立つ人はいますか?
and
は、両方の式が論理的にTrue
であるかどうかをテストしますが、&
(True
/False
の値と共に使用した場合)は両方がTrue
かどうかをテストします。
Pythonでは、通常、空の組み込みオブジェクトは論理的にFalse
として扱われますが、空でない組み込みオブジェクトは論理的にTrue
として扱われます。これにより、リストが空の場合に何かを実行し、リストが空でない場合に他の何かを実行するという一般的なユースケースが容易になります。これは、リスト[False]が論理的にTrue
であることを意味することに注意してください。
>>> if [False]:
... print 'True'
...
True
したがって、例1では、最初のリストは空ではないため、論理的にTrue
であるため、and
の真理値は2番目のリストの真理値と同じです。 (この場合、2番目のリストは空ではないため、論理的にTrue
ですが、それを識別するには不必要な計算ステップが必要になります。)
たとえば、リストには意味のない要素を含めることができるため、リストをビット単位で意味のある形で結合することはできません。ビット単位で組み合わせることができるものには、TrueとFalse、整数が含まれます。
対照的に、NumPyオブジェクトはベクトル化された計算をサポートします。つまり、複数のデータに対して同じ操作を実行できます。
NumPy配列(長さ> 1)には真理値がないため、ベクトルベースのロジックの混乱を防ぐため、例3は失敗します。
例4は、ベクトル化されたビットand
操作です。
ボトムライン
配列を処理せず、整数の数学操作を実行していない場合は、おそらくand
が必要です。
組み合わせたい真理値のベクトルがある場合は、numpy
と&
を使用します。
ショートサーキットのブール演算子(and
、or
)は、新しい言語機能を導入したりショートサーキットを犠牲にしたりすることなく満足のいく方法がないため、オーバーライドできません。ご存じかもしれませんが、真理値の最初のオペランドを評価し、その値に応じて、2番目の引数を評価して返すか、2番目の引数を評価して最初の引数を返さないかのいずれかです。
something_true and x -> x
something_false and x -> something_false
something_true or x -> something_true
something_false or x -> x
(評価の結果)実際のオペランドが返され、その真理値ではないことに注意してください。
動作をカスタマイズする唯一の方法は、__nonzero__
(Python 3の__bool__
に名前を変更)をオーバーライドすることです。これにより、返されるオペランドに影響を与えることができます。リスト(および他のコレクション)は、何かが含まれている場合は「真」であり、空の場合は「偽」であると定義されています。
NumPy配列はその概念を拒否します:目的のユースケースでは、2つの異なる真理の概念が一般的です:(1)いずれかの要素が真であるかどうか、および(2)すべての要素が真であるかどうか。これら2つは完全に(そして静かに)互換性がなく、どちらも明らかに正確または一般的ではないため、NumPyは推測を拒否し、.any()
または.all()
を明示的に使用する必要があります。
&
および|
(およびnot
、ところで)canは短絡しないため、完全にオーバーライドされます。オーバーライドされると、何でも返すことができ、NumPyは、他のスカラー操作と同様に、それを利用して要素ごとの操作を行います。一方、リストは要素間で操作をブロードキャストしません。 mylist1 - mylist2
は何も意味せず、mylist1 + mylist2
はまったく異なるものを意味するように、リストには&
演算子はありません。
list
について最初に非常に重要なポイントで、そこからすべてが続きます(願っています)。
通常のPythonでは、list
は特別なものではありません(ほとんどが歴史的な事故である、構築するためのかわいい構文を持つことを除いて)。リスト[3,2,6]
が作成されると、それはすべての意図と目的のために、番号3
、セット{3,7}
、または関数lambda x: x+5
のような単なる通常のPythonオブジェクトになります。
(はい、要素の変更をサポートし、イテレーション、および他の多くのことをサポートしますが、それは型です:一部の操作をサポートし、他の一部をサポートしません。intは累乗をサポートしますが、そうではありませんラムダは呼び出しをサポートしていますが、それはあまり特別ではありません-結局のところ、ラムダは何のためにあるのですか:).
and
についてand
は演算子ではありません(「演算子」と呼ぶことができますが、「for」も演算子と呼ぶことができます)。 Pythonの演算子は、あるタイプのオブジェクトで呼び出される(実装される)メソッドであり、通常はそのタイプの一部として記述されます。メソッドがそのオペランドの一部の評価を保持する方法はありませんが、and
はそれを実行できます(実行する必要があります)。
その結果、and
がオーバーロードできないように、for
はオーバーロードできません。これは完全に一般的であり、指定されたプロトコルを介して通信します。あなたがcanすることはプロトコルのあなたの部分をカスタマイズすることですが、それはあなたがand
の振る舞いを完全に変えることができることを意味しません。プロトコルは次のとおりです。
Pythonが "a and b"を解釈することを想像してください(これは文字通りこのようにはなりませんが、理解するのに役立ちます)。 「and」に関しては、評価したばかりのオブジェクト(a)を見て、それを尋ねます。あなたは本当ですか? (NOT:are True
?)あなたがクラスの作成者である場合、この回答をカスタマイズできます。 a
が「no」と答えた場合、and
(bを完全にスキップし、まったく評価されず、):a
は私の結果(NOT =:Falseは私の結果です)。
a
が応答しない場合、and
はそれを尋ねます:あなたの長さは? (繰り返しますが、これをa
のクラスの作成者としてカスタマイズできます)。 a
が0と答えた場合、and
は上記と同じ-偽と見なし(NOT False)、bをスキップし、結果としてa
を返します。
a
が2番目の質問(「あなたの長さ」)に0以外の答えを返すか、まったく答えないか、最初の質問に「はい」と答える(「あなたは本当ですか」) 、and
はbを評価し、次のように言います:b
は私の結果です。 NOT ask b
質問をすることに注意してください。
これをすべて言うもう1つの方法は、a and b
がb if a else a
とほぼ同じであるということです。ただし、aは1回だけ評価されます。
ペンと紙で数分間座って、{a、b}が{True、False}のサブセットである場合、ブール演算子に期待されるとおりに動作することを確信してください。しかし、もっと一般的であり、後でお分かりのように、この方法ではるかに便利であると確信したことを願っています。
例1を理解してください。and
は、mylist1が数値、リスト、ラムダ、またはクラスArgmhblのオブジェクトであるかどうかを気にしません。プロトコルの質問に対するmylist1の答えを気にするだけです。そしてもちろん、mylist1は長さに関する質問に5で答え、mylist2を返します。以上です。 mylist1とmylist2の要素とは関係ありません-それらはどこにも絵を入れません。
list
の&
一方、&
は、たとえば+
のような他の演算子と同じです。そのクラスで特別なメソッドを定義することで、型に対して定義できます。 int
はビット単位の「and」として定義され、boolは論理的な「and」として定義されますが、それは1つのオプションに過ぎません。 list
はそれを定義しません。おそらく、Guidoがそれを定義する明白な方法を考えなかったためでしょう。
もう一方の足:-D、numpy配列は特別なであるか、少なくともそうしようとしている。もちろん、numpy.arrayは単なるクラスであり、and
をオーバーライドすることはできません。したがって、次の最善のことを行います。「are you true」と尋ねられた場合、numpy.arrayは、質問を言い換えてください。真実に対する私の見解は、あなたのモデルに当てはまりません。」 (ValueErrorメッセージはand
について語らないことに注意してください-numpy.arrayはwhoが質問をしていることを知らないためです。真実について話すだけです)
&
については、まったく別の話です。 numpy.arrayは、必要に応じて定義でき、他の演算子と一貫して&
を定義します:ポイントワイズ。だから、最終的にあなたが欲しいものを手に入れます。
HTH、
例1:
これは、 and 演算子の動作方法です。
xおよびy=> ifxがfalseの場合、x、elsey
つまり、mylist1
はFalse
ではないため、式の結果はmylist2
になります。 ( 空のリスト のみがFalse
に評価されます。)
例2:
&
演算子はビット単位であり、あなたが言うように。ビット演算は数値に対してのみ機能します。 a&bの結果は、両方が1であるビットの1で構成される数です aおよびb。例えば:
>>> 3 & 1
1
バイナリリテラル (上記と同じ数字)を使用すると、何が起こっているかを簡単に確認できます。
>>> 0b0011 & 0b0001
0b0001
ビット演算は、概念的にはブール(真)演算に似ていますが、ビットに対してのみ機能します。
だから、私の車についていくつかの声明を与えられた
これら2つのステートメントの論理的な「and」は次のとおりです。
(私の車は赤ですか?)および(車には車輪がありますか?)=>論理的に偽の値
少なくとも私の車については、両方とも真実です。したがって、ステートメント全体の値はlogicallytrueです。
これら2つのステートメントのビット単位の「and」は、もう少し曖昧です。
(ステートメント 'my car is red'の数値)&(ステートメント 'my car has wheel'の数値)=> number
pythonがステートメントを数値に変換する方法を知っている場合、変換を行い、2つの値のビットごとのandを計算します。これにより、&
はand
と互換性があると思われるかもしれませんが、上記の例と同様に異なるものです。また、変換できないオブジェクトについては、TypeError
を取得するだけです。
例3および4:
Numpyは、配列に対して 算術演算 を実装します。
Ndarrayの算術演算と比較演算は、要素ごとの演算として定義され、通常は結果としてndarrayオブジェクトを生成します。
python の論理演算子をオーバーロードできないため、配列の論理演算は実装しません。そのため、例3は機能しませんが、例4は機能します。
and
vs &
の質問に答えるには、and
を使用します。
ビット演算は、数値の構造(設定されているビット、設定されていないビット)を調べるために使用されます。この種の情報は、主に低レベルのオペレーティングシステムインターフェイスで使用されます( unix許可ビット など)。ほとんどのpythonプログラムはそれを知る必要はありません。
ただし、論理演算(and
、or
、not
)は常に使用されます。
Pythonでは、X and Y
の式はY
を返します。bool(X) == True
またはX
またはY
のいずれかがFalseに評価される場合、たとえば:
True and 20
>>> 20
False and 20
>>> False
20 and []
>>> []
ビット演算子は、リストに対して単に定義されていません。しかし、整数に対して定義されています-数値のバイナリ表現を操作します。 16(01000)と31(11111)を考えてください:
16 & 31
>>> 16
NumPyは超能力者ではなく、あなたがそれを意味するかどうかは知りません。 [False, False]
は、論理式のTrue
と等しくなければなりません。これでは、標準のPython動作をオーバーライドします。つまり、「len(collection) == 0
を持つ空のコレクションはすべてFalse
」です。
おそらく、NumPyの配列の&演算子の予想される動作です。
最初の例では、 Djangoのドキュメント に基づいています。
常に2番目のリストを返します。実際、空でないリストはPythonのTrue値と見なされるため、pythonは 'last' True値を返すため、2番目のリストはリスト
In [74]: mylist1 = [False]
In [75]: mylist2 = [False, True, False, True, False]
In [76]: mylist1 and mylist2
Out[76]: [False, True, False, True, False]
In [77]: mylist2 and mylist1
Out[77]: [False]
Pythonリストを使用した操作は、リストで実行されます。 list1 and list2
は、list1
が空であるかどうかを確認し、空の場合はlist1
を返し、空でない場合はlist2
を返します。 list1 + list2
はlist2
をlist1
に追加するため、len(list1) + len(list2)
要素を含む新しいリストを取得します。
&
など、要素ごとに適用された場合にのみ意味を持つ演算子は、要素ごとのループなしでは要素ごとの操作がサポートされないため、TypeError
を発生させます。
Numpy配列はelement-wise操作をサポートします。 array1 & array2
は、ビット単位またはarray1
およびarray2
の対応する各要素を計算します。 array1 + array2
は、array1
およびarray2
の対応する各要素の合計を計算します。
これはand
およびor
では機能しません。
array1 and array2
は、本質的に次のコードの短縮形です。
if bool(array1):
return array2
else:
return array1
このためには、bool(array1)
の適切な定義が必要です。 Pythonリストで使用されるようなグローバル操作の場合、定義はlist
が空でない場合はbool(list) == True
、空の場合はFalse
です。 numpyの要素ごとの操作では、要素がTrue
に評価されるかどうかを確認するか、すべての要素がTrue
に評価されるかを明確にする必要があります。どちらもほぼ間違いなく正しいため、bool()
が(間接的に)配列で呼び出された場合、numpyはValueError
を推測せず、発生させます。