pre-increment /decrement演算子を変数(のように)に適用できることに気付きました++count
。コンパイルされますが、実際には変数の値は変更されません。
Pythonでのプリインクリメント/デクリメント演算子(+ +/--)の動作は何ですか?
PythonがC/C ++で見られるこれらの演算子の動作から逸脱するのはなぜですか?
++
演算子ではありません。2つの+
演算子です。+
演算子は恒等演算子であり、何もしません。(明確化:単項演算子+
と-
単項演算子は数値に対してのみ機能しますが、架空の++
演算子が文字列に対して機能することは期待できないと思います。)
++count
として解析
+(+count)
これは
count
+=
やりたいことを行うには、少し長い演算子を使用する必要があります。
count += 1
一貫性と単純さのために、演算子++
と演算子が省略されたのではないかと思います。--
グイド・ヴァン・ロッサムが決定に対して与えた正確な議論はわかりませんが、いくつかの議論を想像することができます。
++count
はあいまいです。これは、+
(2つの単項演算子)と同じくらい簡単に(1つの単項演算子)である可能性があるためです。これは、構文上の重要なあいまいさではありませんが、存在します。+
count
+
++
count
++
++
の同義語にすぎません+= 1
。Cコンパイラは愚かで、ほとんどのコンピュータが持っている命令に最適化a += 1
する方法を知らなかったので、それは速記で発明されました。inc
コンパイラとバイトコード解釈言語を最適化する今日では、プログラマーがコードを最適化できるように言語に演算子を追加することは、特に一貫性があり読みやすいように設計されたPythonのような言語では、通常は嫌われます。++
は、インクリメント前/デクリメント後の演算子の違い(優先順位と戻り値の両方)を混同することです。Pythonは言語の「落とし穴」を排除するのが好きです。Cでのプリインクリメント/ポストインクリメントの優先順位の問題はかなり厄介で、非常に簡単に混乱します。Pythonには、インクリメントの前後の演算子はありません。
Pythonでは、整数は不変です。つまり、それらを変更することはできません。これは、整数オブジェクトを複数の名前で使用できるためです。これを試して:
>>> b = 5
>>> a = 5
>>> id(a)
162334512
>>> id(b)
162334512
>>> a is b
True
上記のaとbは実際には同じオブジェクトです。aをインクリメントすると、bもインクリメントされます。それはあなたが望むものではありません。したがって、再割り当てする必要があります。このような:
b = b + 1
Pythonを使用した多くのCプログラマーはインクリメント演算子を望んでいましたが、その演算子は実際にオブジェクトを再割り当てしている間、オブジェクトをインクリメントしたように見えます。したがって、-=
and+=
演算子を追加すると、より短くなり、b = b + 1
より明確で柔軟性が高くb++
なるため、ほとんどの人は次のようにインクリメントします。
b += 1
に再割り当てさb
れb+1
ます。これはインクリメント演算子ではありません。インクリメントしないためb
、再割り当てされます。
つまり、PythonはCではなく、マシンコードの低レベルのラッパーではなく、増分が意味をなさず、Cの場合ほど必要ではない高水準の動的言語であるため、ここでは動作が異なります。 、たとえば、ループがあるたびにそれらを使用します。
他の答えは、単なる通常のことを示している限りは正しいです+
が(つまり、数字が1つである場合はそのままにしておきます)、何が起こるかを説明していない限り、不完全です。
正確には、toとtoを+x
評価します。x.__pos__()
++x
x.__pos__().__pos__()
私はこのような非常に奇妙な階級構造を想像することができました(子供たち、家でこれをしないでください!):
class ValueKeeper(object):
def __init__(self, value): self.value = value
def __str__(self): return str(self.value)
class A(ValueKeeper):
def __pos__(self):
print 'called A.__pos__'
return B(self.value - 3)
class B(ValueKeeper):
def __pos__(self):
print 'called B.__pos__'
return A(self.value + 19)
x = A(430)
print x, type(x)
print +x, type(+x)
print ++x, type(++x)
print +++x, type(+++x)
--
Pythonには、単項インクリメント/デクリメント演算子( / )はありません++
。代わりに、値をインクリメントするには、
a += 1
ただし、ここでは注意してください。Cから来ている場合、これもPythonでは異なります。Pythonには、Cのような「変数」がありません。代わりに、Pythonは名前とオブジェクトを使用し、Pythonではint
不変です。
だからあなたがすることを言うことができます
a = 1
int
これがPythonで意味することは、値を持つタイプのオブジェクトを作成し、1
それに名前をバインドするa
ことです。オブジェクトはint
値を持つインスタンスで1
あり、名前 a
はそれを参照しています。名前a
とそれが参照するオブジェクトは異なります。
今、あなたがすることを言うことができます
a += 1
sは不変であるためint
、ここで行われることは次のとおりです。
a
(これはint
with idです0x559239eeb380
)0x559239eeb380
(それは1
)int
オブジェクトを作成します2
(オブジェクトIDがあります0x559239eeb3a0
)a
この新しいオブジェクトに名前を再バインドしますa
これでオブジェクトを参照し0x559239eeb3a0
、元のオブジェクト(0x559239eeb380
)は名前で参照されなくなりましたa
。元のオブジェクトを参照している他の名前がない場合は、後でガベージコレクションされます。自分で試してみてください:
a = 1
print(hex(id(a)))
a += 1
print(hex(id(a)))
Pythonにはこれらの演算子はありませんが、本当に必要な場合は、同じ機能を持つ関数を作成できます。
def PreIncrement(name, local={}):
#Equivalent to ++name
if name in local:
local[name]+=1
return local[name]
globals()[name]+=1
return globals()[name]
def PostIncrement(name, local={}):
#Equivalent to name++
if name in local:
local[name]+=1
return local[name]-1
globals()[name]+=1
return globals()[name]-1
使用法:
x = 1
y = PreIncrement('x') #y and x are both 2
a = 1
b = PostIncrement('a') #b is 1 and a is 2
ローカル変数を変更する場合は、関数内で2番目の引数としてlocals()を追加する必要があります。そうしないと、グローバルを変更しようとします。
x = 1
def test():
x = 10
y = PreIncrement('x') #y will be 2, local x will be still 10 and global x will be changed to 2
z = PreIncrement('x', locals()) #z will be 11, local x will be 11 and global x will be unaltered
test()
また、これらの機能を使用すると、次のことができます。
x = 1
print(PreIncrement('x')) #print(x+=1) is illegal!
しかし、私の意見では、次のアプローチの方がはるかに明確です。
x = 1
x+=1
print(x)
デクリメント演算子:
def PreDecrement(name, local={}):
#Equivalent to --name
if name in local:
local[name]-=1
return local[name]
globals()[name]-=1
return globals()[name]
def PostDecrement(name, local={}):
#Equivalent to name--
if name in local:
local[name]-=1
return local[name]+1
globals()[name]-=1
return globals()[name]+1
モジュールでこれらの関数を使用して、javascriptをpythonに変換しました。
Pythonでは、Common Lisp、Scheme、Rubyなどの言語とは対照的に、式とステートメントの区別が厳密に適用されます。
したがって、このような演算子を導入することで、式とステートメントの分割を破ることができます。
同じ理由であなたは書くことができません
if x = 0:
y = 1
そのような区別が保持されていない他のいくつかの言語でできるように。
Python 3.8以降では、次のことができます。
(a:=a+1) #same as ++a (increment, then return new value)
(a:=a+1)-1 #same as a++ (return the incremented value -1) (useless)
これでたくさんのことを考えることができます。
>>> a = 0
>>> while (a:=a+1) < 5:
print(a)
1
2
3
4
または、より洗練された構文で何かを書きたい場合(目標は最適化ではありません):
>>> del a
>>> while (a := (a if 'a' in locals() else 0) + 1) < 5:
print(a)
1
2
3
4
'a'がエラーなしで存在しなくても0を返し、1に設定します
ええ、私は++と--の機能も逃しました。数百万行のcコードが、私の古い頭の中にそのような考え方を染み込ませており、それと戦うのではなく...これが私が作り上げたクラスです。
pre- and post-increment, pre- and post-decrement, addition,
subtraction, multiplication, division, results assignable
as integer, printable, settable.
ここで'tis:
class counter(object):
def __init__(self,v=0):
self.set(v)
def preinc(self):
self.v += 1
return self.v
def predec(self):
self.v -= 1
return self.v
def postinc(self):
self.v += 1
return self.v - 1
def postdec(self):
self.v -= 1
return self.v + 1
def __add__(self,addend):
return self.v + addend
def __sub__(self,subtrahend):
return self.v - subtrahend
def __mul__(self,multiplier):
return self.v * multiplier
def __div__(self,divisor):
return self.v / divisor
def __getitem__(self):
return self.v
def __str__(self):
return str(self.v)
def set(self,v):
if type(v) != int:
v = 0
self.v = v
あなたはそれをこのように使うかもしれません:
c = counter() # defaults to zero
for listItem in myList: # imaginary task
doSomething(c.postinc(),listItem) # passes c, but becomes c+1
...すでにcを持っているので、これを行うことができます...
c.set(11)
while c.predec() > 0:
print c
....あるいは単に...
d = counter(11)
while d.predec() > 0:
print d
...そして整数への(再)割り当てのために...
c = counter(100)
d = c + 223 # assignment as integer
c = c + 223 # re-assignment as integer
print type(c),c # <type 'int'> 323
...これはタイプカウンターとしてcを維持しますが:
c = counter(100)
c.set(c + 223)
print type(c),c # <class '__main__.counter'> 323
編集:
そして、このちょっとした予期しない(そして完全に望ましくない)振る舞いがあります、
c = counter(42)
s = '%s: %d' % ('Expecting 42',c) # but getting non-numeric exception
print s
...そのタプル内では、getitem()が使用されていないため、代わりにオブジェクトへの参照がフォーマット関数に渡されます。はぁ。それで:
c = counter(42)
s = '%s: %d' % ('Expecting 42',c.v) # and getting 42.
print s
...または、より冗長に、そして明示的に、実際に何をしたいのかを明示的に示しますが、実際の形式では冗長性によって逆に示されます(c.v
代わりに使用してください)...
c = counter(42)
s = '%s: %d' % ('Expecting 42',c.__getitem__()) # and getting 42.
print s
Cのような言語のように、Pythonにはpost/preインクリメント/デクリメント演算子はありません。
数学(-1)*(-1)=(+1)のように、複数の符号が乗算されるのを見ることができます++
。--
例えば
---count
として解析
-(-(-count)))
これは
-(+count)
-
なぜなら、符号と-
符号の乗算は+
そして最後に、
-count
簡単な回避策
c = 0
c = (lambda c_plusplus: plusplus+1)(c)
print(c)
1
これ以上タイピングする必要はありません
c = c + 1
また、c ++を記述してすべてのコードを完成させてから、「c ++」を検索/置換し、「c = c+1」に置き換えることもできます。正規表現検索がオフになっていることを確認してください。