これら2行のコードの違いは何ですか?
if not x == 'val':
そして
if x != 'val':
一方が他方より効率的ですか?
使うほうがいいですか
if x == 'val':
pass
else:
2つのバージョン用に生成されたバイトコードを調べるには、 dis
を使用します。
not ==
4 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 2 (==)
9 UNARY_NOT
10 RETURN_VALUE
!=
4 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 3 (!=)
9 RETURN_VALUE
後者の方が操作が少ないため、わずかに効率的になる可能性があります。
if foo != bar
とif not foo == bar
の操作数が全く同じであることがCOMPARE_OP
が変更されPOP_JUMP_IF_TRUE
がPOP_JUMP_IF_FALSE
に切り替わるだけであることが、コミットメント= /( @Quincunx )で @Quincunx )で指摘されました。
not ==
:
2 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 2 (==)
9 POP_JUMP_IF_TRUE 16
!=
2 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 3 (!=)
9 POP_JUMP_IF_FALSE 16
この場合、各比較に必要な作業量に違いがない限り、パフォーマンスの違いがまったく発生しないとは考えにくいです。
ただし、2つのバージョン は、問題のオブジェクトに対する__eq__
および__ne__
の実装に依存するため、常に論理的に同一であるとは限りません 。 Per データモデルドキュメント :
比較演算子の間に暗黙の関係はありません。
x==y
の真実はx!=y
が偽であることを意味しません。
例えば:
>>> class Dummy(object):
def __eq__(self, other):
return True
def __ne__(self, other):
return True
>>> not Dummy() == Dummy()
False
>>> Dummy() != Dummy()
True
最後に、そしておそらく最も重要なことです。一般的に、2つのareが論理的に同一の場合、x != y
はnot x == y
よりもはるかに読みやすくなります。
@jonrsharpeは何が起こっているのかについて優れた説明をしています。 3つのオプションをそれぞれ100万回実行したときの時間差を表示するだけでいいと思いました(表示するにはわずかな違いがあります)。
使用コード
def a(x):
if x != 'val':
pass
def b(x):
if not x == 'val':
pass
def c(x):
if x == 'val':
pass
else:
pass
x = 1
for i in range(10000000):
a(x)
b(x)
c(x)
そしてcProfileプロファイラーの結果:
そのため、if not x == 'val':
とif x != 'val':
の間には、0.7%程度のごくわずかな違いがあることがわかります。これらのうち、if x != 'val':
が最速です。
しかし、最も驚くべきことに、我々はそれを見ることができます
if x == 'val':
pass
else:
実際には最速で、if x != 'val':
を最大0.3%上回ります。これはあまり読みやすいものではありませんが、ごくわずかなパフォーマンスの向上を望んでいるのであれば、この道をたどることができるでしょう。
最初に、Pythonは必要以上にもう1つの操作を実行しなければなりません(それが等しくないことを単にチェックする代わりに、それが等しいことが真実でないかどうか、したがってもう1つの操作をチェックしなければなりません)。 1回の実行との違いを見分けることは不可能ですが、何度も実行すると、2回目の実行がより効率的になります。全体的には2番目のものを使用しますが、数学的には同じです。
>>> from dis import dis
>>> dis(compile('not 10 == 20', '', 'exec'))
1 0 LOAD_CONST 0 (10)
3 LOAD_CONST 1 (20)
6 COMPARE_OP 2 (==)
9 UNARY_NOT
10 POP_TOP
11 LOAD_CONST 2 (None)
14 RETURN_VALUE
>>> dis(compile('10 != 20', '', 'exec'))
1 0 LOAD_CONST 0 (10)
3 LOAD_CONST 1 (20)
6 COMPARE_OP 3 (!=)
9 POP_TOP
10 LOAD_CONST 2 (None)
13 RETURN_VALUE
ここで、not x == y
はx != y
よりももう1つ命令が多いことがわかります。そのため、何百万もの比較を行っていない限り、ほとんどの場合、パフォーマンスの違いはごくわずかになります。それでも、これがボトルネックの原因になることはおそらくありません。
追加の注意点は、他の答えはあなたの質問にほとんど正しく答えたので、クラスが__eq__()
だけを定義し、__ne__()
を定義しない場合、あなたのCOMPARE_OP (!=)
は__eq__()
を実行しそれを否定するということです。その時点では、3番目の選択肢は少し効率的になる可能性がありますが、速さが必要な場合にのみ考慮する必要があります。すぐに理解するのは難しいからです。
それはあなたの読み方です。 not
演算子は動的です、それであなたがそれを適用することができる理由
if not x == 'val':
しかし、!=
は、==
が行うこととは反対のことを行う演算子として、より良いコンテキストで読むことができます。
私は上記の私の読みやすさのコメントを拡大したいです。
繰り返しになりますが、読みやすさが他の(パフォーマンスに重要ではない)懸念を上書きすることに完全に同意します。
私が指摘したいのは、脳が「否定的」よりも速く「肯定的」を解釈することです。たとえば、「停止する」と「行かない」(単語数の違いによるかなりお粗末な例)。
だから選択を与えられた:
if a == b
(do this)
else
(do that)
機能的に等価なものよりも好ましいです。
if a != b
(do that)
else
(do this)
読みやすさや理解しやすさが低いと、バグが増えます。おそらく最初のコーディングではありませんが、(あなたほどスマートではありません!)メンテナンスの変更は...