如何使用预增/减运算符(++
、--
),就像在 C++ 中一样?
为什么 ++count
运行,但不改变变量的值?
sta x++
...生成的原子指令将 a
累加器存储在 x
指向的位置,然后将 x
增加累加器的大小。这样做是因为它比指针算术更快,因为它很常见,而且很容易理解。前期和后期。
++
不是运算符。它是两个 +
运算符。 +
运算符是 identity 运算符,它什么都不做。 (澄清:+
和 -
一元运算符仅适用于数字,但我认为您不会期望假设的 ++
运算符适用于字符串。)
++count
解析为
+(+count)
这转化为
count
您必须使用稍长的 +=
运算符来执行您想要执行的操作:
count += 1
我怀疑 ++
和 --
运算符是为了一致性和简单性而被省略的。我不知道 Guido van Rossum 给出的决定的确切论点,但我可以想象一些论点:
解析更简单。从技术上讲,解析 ++count 是不明确的,因为它可以是 +、+、count(两个一元 + 运算符),就像它可以是 ++、count(一个一元 ++ 运算符)一样容易。这不是一个重要的句法歧义,但它确实存在。
更简单的语言。 ++ 只不过是 += 1 的同义词。它是一种简写,因为 C 编译器很愚蠢,不知道如何将 += 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 程序员想要一个自增运算符,但该运算符看起来像是在递增对象,而实际上是在重新分配对象。因此,添加的 -=
和 +=
运算符比 b = b + 1
短,同时比 b++
更清晰、更灵活,因此大多数人会增加:
b += 1
这会将 b
重新分配给 b+1
。那不是增量运算符,因为它不增加 b
,而是重新分配它。
简而言之:Python 在这里表现不同,因为它不是 C,也不是机器代码的低级包装器,而是高级动态语言,其中增量没有意义,也不像 C 中那样必要,例如,每次有循环时都使用它们。
i++
意味着将 i + 1
分配给 变量 i
。 i = 5; i++
表示将 6
分配给 i
,而不是修改 i
指向的 int
对象。也就是说,它并不意味着increment the value of 5
!
i++
仅对左值进行操作。如果它打算增加 i
指向的对象,则此限制将是不必要的。
++
与 += 1
的纯句法选择几乎是正交的。
虽然其他答案是正确的,因为它们显示了仅 +
通常会做什么(即,保持数字不变,如果它是 1),它们是不完整的,因为它们没有解释会发生什么.
准确地说,+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)
TL;博士
Python 没有一元递增/递减运算符 (--
/++
)。相反,要增加一个值,请使用
a += 1
更多细节和陷阱
但是这里要小心。如果你来自 C,即使这在 python 中也是不同的。 Python 没有 C 中的“变量”,而是 Python 使用 names 和 objects,并且在 Python 中 int
是不可变的。
所以让我们说你做
a = 1
这在 python 中的含义是:创建一个类型为 int
的对象,其值为 1
,并将名称 a
绑定到它。 object 是具有值 1
的 int
的实例,name a
引用它。名称 a
和它所指的对象是不同的。
现在让我们说你做
a += 1
由于 int
是不可变的,因此这里发生的情况如下:
查找 a 所指的对象(它是一个 id 为 0x559239eeb380 的 int)查找对象 0x559239eeb380 的值(它是 1)将该值加 1 (1 + 1 = 2) 创建一个值为 2 的新 int 对象(它的对象 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
如果要更改局部变量,则必须在函数内部添加 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 的模块中使用了这些函数。
与 Common Lisp、Scheme 或 Ruby 等语言相比,Python 严格区分表达式和语句。
因此,通过引入此类运算符,您将打破表达式/语句的拆分。
出于同样的原因,你不能写
if x = 0:
y = 1
就像在其他一些没有保留这种区别的语言中一样。
if (n := len(a)) > 10: y = n + 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
a++
会增加但返回旧值,(a:=a+1)
更像是 ++a
会增加并返回新值。
(a:+=1)
吗?
是的,我也错过了 ++ 和 -- 功能。几百万行 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
python中没有像C这样的语言那样的post/pre递增/递减运算符。
我们可以将 ++
或 --
视为多个符号相乘,就像我们在数学 (-1) * (-1) = (+1) 中所做的那样。
例如
---count
解析为
-(-(-count)))
这转化为
-(+count)
因为,-
符号与 -
符号的乘积是 +
最后,
-count
-----count
时会发生什么。
扩展 Henry 的答案,我实验性地实现了一个语法糖库,实现了 a++
:hdytto。
用法很简单。从 PyPI 安装后,放置 sitecustomize.py
:
from hdytto import register_hdytto
register_hdytto()
在您的项目目录中。然后,制作 main.py
:
# coding: hdytto
a = 5
print(a++)
print(++a)
b = 10 - --a
print(b--)
并由 PYTHONPATH=. python main.py
运行。输出将是
5
7
4
hdytto 在解码脚本文件时将 a++
替换为 ((a:=a+1)-1)
,因此它可以工作。
一个直接的解决方法
c = 0
c = (lambda c_plusplus: plusplus+1)(c)
print(c)
1
不再打字
c = c + 1
此外,您可以只编写 c++ 并完成所有代码,然后搜索/替换“c++”,替换为“c=c+1”。只要确保正则表达式搜索已关闭。
if (c++): ...
不定期副业成功案例分享
+
运算符很有用。对于 decimal.Decimal 对象,它会四舍五入到当前精度。+ +
和++
的歧义。++
只不过是+= 1
的同义词是不正确的。 ++ 有前增量和后增量的变体,所以它显然不是一回事。不过,我同意你的其他观点。