970

我注意到可以在变量(如++count)上应用预递增/递减运算符。它可以编译,但实际上并没有改变变量的值!

Python 中预递增/递减运算符 (++/--) 的行为是什么?

为什么 Python 会偏离 C/C++ 中看到的这些运算符的行为?

4

10 回答 10

1253

++不是运算符。它是两个+运算符。+运算符是身份运算符,它什么也不做。(澄清:+-一元运算符仅适用于数字,但我认为您不会期望假设++运算符适用于字符串。)

++count

解析为

+(+count)

这转化为

count

您必须使用稍长的+=运算符来执行您想要执行的操作:

count += 1

我怀疑++and--运算符是为了一致性和简单性而被排除在外的。我不知道 Guido van Rossum 给出的决定的确切论点,但我可以想象一些论点:

  • 解析更简单。从技术上讲,解析++count是模棱两可的,因为它可能是+, +, count(两个一元运算符),就像它可能是, (一个一元运算+符)一样容易。这不是一个重要的句法歧义,但它确实存在。++count++
  • 更简单的语言。++无非是 的同义词+= 1。它是一种速记发明,因为 C 编译器很愚蠢,不知道如何优化a += 1inc大多数计算机的指令。在优化编译器和字节码解释语言的今天,向一种语言添加运算符以允许程序员优化他们的代码通常是不受欢迎的,尤其是在像 Python 这样的设计为一致和可读的语言中。
  • 令人困惑的副作用。使用运算符的语言中一个常见的新手错误++是混淆了前后递增/递减运算符之间的差异(优先级和返回值),而 Python 喜欢消除语言“陷阱”。C 中的前/后增量优先级问题非常棘手,而且非常容易搞砸。
于 2009-09-28T07:39:36.027 回答
425

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

哪个将重新分配bb+1. 那不是增量运算符,因为它不增量b,它重新分配它。

简而言之:Python 在这里表现不同,因为它不是 C 语言,也不是机器代码的低级包装器,而是高级动态语言,增量没有意义,也不像 C 语言那样必要,例如,每次有循环时都使用它们。

于 2009-09-28T09:05:01.030 回答
56

虽然其他答案是正确的,因为它们显示了单纯的+通常所做的事情(即,保持数字不变,如果它是一个),但就它们没有解释发生的情况而言,它们是不完整的。

准确地说,+x求值 tox.__pos__()++xto 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)
于 2012-06-26T14:59:33.913 回答
22

TL;博士

Python 没有一元递增/递减运算符 ( --/ ++)。相反,要增加一个值,请使用

a += 1

更多细节和陷阱

但是这里要小心。如果你来自 C,即使这在 python 中也是不同的。在 C 的意义上,Python 没有“变量”,而是 python 使用名称对象,并且在 pythonint中是不可变的。

所以让我们说你做

a = 1

这在 python 中的含义是:创建一个int具有值的类型的对象1并将名称绑定a到它。该对象int具有 value的实例1,并且名称 a引用它。a它所指的名称和对象是不同的。

现在让我们说你做

a += 1

由于ints 是不可变的,所以这里发生的情况如下:

  1. 查找a引用的对象(它是int带有 id的0x559239eeb380
  2. 查找对象的值0x559239eeb380(它是1
  3. 将该值加 1 (1 + 1 = 2)
  4. 创建一个具有值的 对象(它具有对象 id )int20x559239eeb3a0
  5. 将名称重新绑定a到这个新对象
  6. 现在a指的是 object 0x559239eeb3a0,而原来的 object( 0x559239eeb380) 不再由名称来指代a。如果没有任何其他名称引用原始对象,则稍后将对其进行垃圾收集。

自己试一试:

a = 1
print(hex(id(a)))
a += 1
print(hex(id(a)))
于 2019-01-18T16:31:24.460 回答
14

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 的模块中使用了这些函数。

于 2014-10-05T15:37:15.257 回答
12

与 Common Lisp、Scheme 或 Ruby 等语言相比,Python 严格区分表达式和语句。

维基百科

因此,通过引入此类运算符,您将打破表达式/语句的拆分。

出于同样的原因,你不能写

if x = 0:
  y = 1

就像在其他一些没有保留这种区别的语言中一样。

于 2011-12-16T16:33:46.587 回答
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

于 2020-05-29T11:33:26.250 回答
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
于 2015-05-19T20:40:43.847 回答
2

python中没有像C这样的语言那样的post/pre递增/递减运算符。

我们可以看到++多个--符号相乘,就像我们在数学中所做的那样 (-1) * (-1) = (+1)。

例如

---count

解析为

-(-(-count)))

这转化为

-(+count)

-因为,符号与-符号的乘积是+

最后,

-count
于 2020-01-14T18:04:42.337 回答
-1

一个直接的解决方法

c = 0
c = (lambda c_plusplus: plusplus+1)(c)
print(c)
1

不再打字

 c = c + 1

此外,您可以只编写 c++ 并完成所有代码,然后搜索/替换“c++”,替换为“c=c+1”。只要确保正则表达式搜索已关闭。

于 2021-03-12T19:07:06.990 回答