++count
)。它可以编译,但实际上并没有改变变量的值!Python中预增/减算符(++ /-)的行为是什么?
为什么Python会偏离在C / C ++中看到的这些运算符的行为?
#1 楼
++
不是运算符。它是两个+
运算符。 +
运算符是身份运算符,不执行任何操作。 (澄清:+
和-
一元运算符仅适用于数字,但我想您不会期望假设的++
运算符适用于字符串。)++count
解析为
+(+count)
翻译为
count
您必须使用稍长的
+=
运算符来执行您想做什么:count += 1
我怀疑
++
和--
运算符出于一致性和简单性而被忽略了。我不知道Guido van Rossum做出决定的确切论据,但我可以想象几个论点:更简单的解析。从技术上讲,解析
++count
是模棱两可的,因为它可能像+
,+
(一个一元count
运算符)一样容易地被解析为+
,++
,count
(两个一元++
运算符)。它不是语法上的明显歧义,但确实存在。简单的语言。
++
只是+= 1
的同义词。因为C编译器很笨,并且不知道如何将a += 1
优化为大多数计算机具有的inc
指令,所以发明了这种简写。在优化编译器和字节码解释语言的这一天,通常不赞成在一种语言中添加运算符以允许程序员优化其代码,尤其是在像Python这样设计成一致且易读的语言中。令人困惑的是,效果。使用
++
运算符的语言中一个常见的新手错误是混淆前后递增/递减运算符之间的差异(优先级和返回值),Python喜欢消除语言“陷阱”。 C中的预增/后增的优先级问题非常棘手,难以置信。评论
“ +运算符是“身份”运算符,它什么也不做。”仅适用于数字类型;对于其他类型,默认情况下为错误。
– newacct
09年9月28日在7:47
另外,请注意,在Python中,+ =和friends不是可在表达式中使用的运算符。而是在Python中将它们定义为“增强的赋值语句”的一部分。这与Python中的语言设计决策一致,即不允许在任意表达式中将赋值(“ =”)作为运算符,这与C语言中的操作不同。请参阅docs.python.org/reference/…
–内德·迪利(Ned Deily)
09年9月28日在8:06
一元+运算符有用途。对于decimal.Decimal对象,将四舍五入到当前精度。
–u0b34a0f6ae
09年9月28日在9:10
我押注解析器的简化。请注意PEP 3099中的一个项目,“ Python 3000中不会改变的事情”:“解析器不会比LL(1)复杂。简单总比复杂要好。这个想法扩展到了解析器。Python的语法仅限于LL(1)解析器是一种福气,而不是诅咒。它把我们带到手铐上,可以防止我们过度学习并最终得到时髦的语法规则,如一些其他不愿透露名称的动态语言,例如Perl。”我看不出如何在不破坏LL(1)的情况下消除++和++的歧义。
–迈克·德西蒙(Mike DeSimone)
2010-10-14 19:42
说++只不过是+ = 1的同义词是不正确的。++存在++的递增和递减变体,因此显然不是同一回事。不过,我同意您的其他观点。
– PhilHibbs
17年5月17日在8:59
#2 楼
当您想增加或减少时,通常需要对整数进行操作。像这样: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中那样必要,因为C在每次循环时都使用它们。
评论
这个例子是错误的(您可能会将不变性与身份混淆)-由于某些虚拟机优化,它们使用相同的对象直到255(或类似的数字),因此它们具有相同的ID。例如(更大的数字):>>> a = 1231231231231 >>> b = 1231231231231 >>> id(a),id(b)(32171144,32171168)
–ionelmc
2010年1月11日下午1:00
不变性主张是虚假的。从概念上讲,i ++意味着将i + 1分配给变量i。 i = 5; i ++的意思是给i赋6,而不修改i指向的int对象。也就是说,这并不意味着增加5的值!
–机械蜗牛
2011-09-20 4:19
@机械蜗牛:在这种情况下,它根本不是增量运算符。然后+ =运算符会更清晰,更明确,更灵活,并且仍然可以执行相同的操作。
– Lennart Regebro
2011-09-20 5:35
@LennartRegebro:在C ++和Java中,i ++仅在左值上运行。如果要增加i指向的对象,则此限制是不必要的。
–机械蜗牛
2011-09-20 7:18
我觉得这个答案令人困惑。为什么要假设++意味着+ = 1的简写呢?这正是C语言的含义(假设未使用返回值)。您似乎想出了一些其他含义。
–唐·哈奇
18年7月13日在0:32
#3 楼
尽管其他答案在表明仅+
通常会做的事情上是正确的(即,将数字保留为原来的数字,如果是一个数字),但在不解释会发生什么的情况下,它们是不完整的。 br /> 确切地说,
+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)
#4 楼
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的模块中使用了这些功能。
评论
注意:虽然很棒,但是如果您的本地人存在于类函数堆栈框架中,则这些帮助程序方法将不起作用。即-从类方法def中调用它们将不起作用-'locals()'dict是快照,并且不会更新堆栈框架。
–亚当
19年6月14日在5:49
#5 楼
TL; DRPython没有一元增减运算符(
--
/ ++
)。相反,要增加值,请使用a += 1
更多详细信息和注意事项
,但请注意此处。如果您来自C,即使在python中也是如此。 Python在C的意义上没有“变量”,而是python使用名称和对象,而在python中
int
是不可变的。所以可以说你做到了
a = 1
这在python中的含义是:创建一个具有值
int
的1
类型的对象,并将名称a
绑定到该对象。该对象是具有值int
的1
的实例,名称a
引用了该对象。名称a
和它所引用的对象是不同的。现在让您说您做
a += 1
由于
int
是不可变的,所以发生以下情况:查找
a
所指的对象(它是ID为int
的0x559239eeb380
)查找对象
0x559239eeb380
的值(它是1
)将1添加到该值(1 + 1 = 2)
创建一个新的
int
对象,其值为2
(其对象ID为0x559239eeb3a0
)将名称
a
绑定到该新对象现在,
a
引用对象0x559239eeb3a0
,并且不再用名称0x559239eeb380
引用原始对象(a
)。如果没有其他名称引用原始对象,则稍后将对其进行垃圾回收。尝试一下:
a = 1
print(hex(id(a)))
a += 1
print(hex(id(a)))
#6 楼
在Python中,与Common Lisp,Scheme或
Wikipedia
因此,通过引入此类运算符,您可以打破表达式/语句的分裂。
出于相同的原因,您不能像其他某些语言中那样,不能写
if x = 0:
y = 1
。
/>
评论
有趣的是,此限制将在即将发布的Python 3.8中取消,并带有赋值表达式的新语法(PEP-572 python.org/dev/peps/pep-0572)。例如,如果(n:= len(a))> 10:y = n + 1,我们将能够写出。请注意,这种区别很明显,因为为此引入了新的运算符(:=)
– Zertrin
19年8月26日在9:34
#7 楼
是的,我也错过了++和-功能。数百万行的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
#8 楼
在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
评论
伟大的答案!只是一个建议:a ++会增加但返回旧值,(a:= a + 1)更像是++ a会增加并返回新值。
–thiagola92
7月20日,0:41
#9 楼
python中没有像C之类的语言中的post / pre递增/ decre减运算符。我们可以看到
++
或--
是多个符号相乘,就像我们在数学(-1)中一样*(- 1)=(+1)。例如
---count
解析为
-(-(-count)))
哪个翻译成
-(+count)
,因为
-
符号与-
符号的乘积是+
最后,
-count
评论
这说明其他答案没有什么意思?
–丹尼尔·B。
1月14日18:26
@DanielB。其他答案尚未说明内部发生了什么。而且,他们都没有告诉您写----- count时会发生什么。
–Optider
1月16日0:47
没有提及正在执行乘法运算,因此,我认为有一个要点,直截了当的答案对于其他用户很有用。如果您从中了解的话,就没有冒犯。学习比学习的来源更重要。
–Optider
1月17日4:15
评论
Python不是C或C ++。语言的设计决策各不相同。特别是,Python故意没有定义可在任意表达式中使用的赋值运算符。而是有赋值语句和扩充的赋值语句。请参阅下面的参考。是什么让您认为python具有++和-运算符?
Kaizer:来自C / C ++,我编写++ count,它在Python中编译。因此,我认为该语言具有运算符。
@Fox您假设没有明显的计划和组织水平
@mehaase ++和-在c中不存在“作为指针算术的语法糖”,它们之所以存在,是因为许多处理器将自动递增和递减内存访问机制(通常是指针索引,堆栈索引)作为其本机指令的一部分组。例如,在6809汇编程序中:sta x ++ ...产生的原子指令将x指向的累加器存储在其中,然后将x累加到累加器的大小。这样做是因为它比指针算术更快,因为它很常见,并且很容易理解。前和后。