问题:Python中递增和递减运算符的行为
我注意到,可以对变量(如++count
)应用预增减算符。它可以编译,但实际上并不会改变变量的值!
Python中预增/减运算符(++ /-)的行为是什么?
为什么Python会偏离C / C ++中看到的这些运算符的行为?
回答 0
++
不是运算符。它是两个+
运算符。该+
运营商的身份运营,这什么都不做。(澄清:the +
和-
一元运算符仅对数字起作用,但是我假设您不会期望假设的++
运算符对字符串起作用。)
++count
解析为
+(+count)
转化为
count
您必须使用稍长的+=
运算符来完成您想做的事情:
count += 1
我怀疑++
和--
运算符因一致性和简单性而被遗漏了。我不知道Guido van Rossum做出决定的确切论据,但我可以想象一些论点:
- 更简单的解析。从技术上讲,解析
++count
是模糊的,因为它可能是+
,+
,count
(两个一元+
经营者)一样容易,因为它可能是++
,count
(一个一元++
运算符)。它不是语法上的明显歧义,但确实存在。 - 语言更简单。
++
只不过是的同义词+= 1
。这是一种速记方法,因为C编译器很愚蠢,并且不知道如何优化大多数计算机所拥有a += 1
的inc
指令。在优化编译器和字节码解释语言的这一天,通常不赞成在一种语言中添加运算符以允许程序员优化其代码,尤其是在像Python这样设计成一致且易读的语言中。 - 令人困惑的副作用。带有
++
运算符的语言中一个常见的新手错误是将递增/递减运算符前后的差异(优先级和返回值)混合在一起,Python喜欢消除语言“陷阱”。该优先事项的用C前置/后置增量是相当毛,和令人难以置信的容易陷入困境。
回答 1
当您想增加或减少时,通常需要对整数进行操作。像这样:
b++
但是在Python中,整数是不可变的。那是你不能改变他们。这是因为可以使用多个名称使用整数对象。尝试这个:
>>> b = 5
>>> a = 5
>>> id(a)
162334512
>>> id(b)
162334512
>>> a is b
True
上面的a和b实际上是同一对象。如果增加a,也将增加b。那不是你想要的。因此,您必须重新分配。像这样:
b = b + 1
或更简单:
b += 1
哪个将重新分配b
给b+1
。那不是增量运算符,因为它不会增量b
,而是重新分配它。
简而言之:Python在这里的行为有所不同,因为它不是C,也不是机器代码的底层包装,而是高级动态语言,在这种语言中,增量没有意义,也没有C所必需,例如,每次有循环时在哪里使用它们。
回答 2
尽管其他答案在表明仅仅+
做某事上是正确的(即,保留数字,如果是一个,则保持不变),但就他们不解释会发生什么而言,它们是不完整的。
确切地说,+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)
回答 3
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
在函数内部,如果要更改局部变量,则必须添加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的模块中使用了这些功能。
回答 4
在Python中,与Common Lisp,Scheme或Ruby之类的语言相比,严格执行了表达式和语句之间的区别。
因此,通过引入此类运算符,可以打破表达式/语句的拆分。
出于同样的原因,你不能写
if x = 0:
y = 1
就像其他一些语言一样,这种语言没有保留。
回答 5
TL; DR
Python没有一元增减运算符(--
/ ++
)。相反,要增加值,请使用
a += 1
更多细节和陷阱
但是请注意这里。如果您来自C,即使在python中也是如此。在C的意义上,Python没有“变量”,而是python使用名称和对象,并且在int
s中是不可变的。
所以说你做
a = 1
这在python中的含义是:创建一个int
具有值的类型的对象,1
并将名称绑定a
到该对象。的对象是的一个实例int
具有值1
,并且名称 a
是指它。名称a
和它引用的对象是不同的。
现在说你做
a += 1
由于int
s是不可变的,因此这里发生的情况如下:
- 查找所
a
引用的对象(int
ID为0x559239eeb380
) - 查找对象的值
0x559239eeb380
(为1
) - 给该值加1(1 +1 = 2)
- 创建一个具有值的新
int
对象2
(它具有对象id0x559239eeb3a0
) - 将名称重新绑定
a
到这个新对象 - 现在
a
引用对象,0x559239eeb3a0
并且0x559239eeb380
名称不再引用原始对象()a
。如果没有其他名称引用原始对象,则稍后将对其进行垃圾回收。
自己尝试一下:
a = 1
print(hex(id(a)))
a += 1
print(hex(id(a)))
回答 6
是的,我也错过了++和-功能。几百万行c代码使这种思想深深地扎根在我的脑海中,而不是与之抗争……这是我拼凑而成的一类,实现了:
pre- and post-increment, pre- and post-decrement, addition,
subtraction, multiplication, division, results assignable
as integer, printable, settable.
这是:
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
回答 7
python中没有像C之类的语言中的post / pre增量/减量运算符。
我们可以看到++
或--
随着多个符号成倍增加,就像我们在数学(-1)*(-1)=(+1)中一样。
例如
---count
解析为
-(-(-count)))
转化为
-(+count)
因为,-
符号与-
符号的乘积为+
最后,
-count
回答 8
在python 3.8+中,您可以执行以下操作:
(a:=a+1) #same as a++
您可以对此进行很多思考。
>>> 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
如果不存在任何错误,它将很好地返回0,然后将其设置为1