Python中递增和递减运算符的行为

时间:2009-09-28 07:33:20

标签: python operators increment decrement

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

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

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

10 个答案:

答案 0 :(得分:909)

++不是运营商。它是两个+运算符。 +运算符是标识运算符,它不执行任何操作。 (澄清:+-一元运算符只适用于数字,但我认为你不会指望一个假设的++运算符来处理字符串。)

++count

解析为

+(+count)

转换为

count

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

count += 1

我怀疑++--运算符是为了保持一致性和简单性而被遗漏的。我不知道Guido van Rossum给出的确切论据,但我可以想象一些论点:

  • 更简单的解析。从技术上讲,解析++count是不明确的,因为它可能是++count(两个一元+运算符),就像它可能一样容易{ {1}},++(一个一元count运营商)。这不是一个重要的句法歧义,但确实存在。
  • 语言比较简单。 ++只不过是++的同义词。这是一个发明的简写,因为C编译器很愚蠢,并且不知道如何将+= 1优化到大多数计算机具有的a += 1指令中。在优化编译器和字节码解释语言的今天,将语言添加到语言以允许程序员优化代码通常是不受欢迎的,特别是在像Python这样设计为一致且可读的语言中。
  • 令人困惑的副作用。使用inc运算符的语言中的一个常见新手错误正在混合前后增量/减量运算符之间的差异(优先级和返回值),Python喜欢消除语言“gotcha”-s 。 precedence issuespre-/post-increment in C非常多毛,非常容易搞砸。

答案 1 :(得分:364)

当您想要递增或递减时,通常希望对整数执行此操作。像这样:

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 :(得分:48)

虽然其他人的回答是正确的,因为他们只显示+通常做的事情(即,保留数字,如果它是一个),他们是不完整的,因为他们不解释会发生什么。

确切地说,+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 :(得分:9)

  

在Python中,表达式和语句之间的区别是严格的   强制执行,与Common Lisp,Scheme或   红宝石。

Wikipedia

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

出于同样的原因你不能写

if x = 0:
  y = 1

正如你可以在其他语言中那样,不保留这种区别。

答案 4 :(得分:9)

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。

答案 5 :(得分:4)

是的,我也错过了++和 - 功能。几百万行的c代码在我的老头脑中刻上了那种想法,而不是与它作斗争......这是我练习的一个类:

pre- and post-increment, pre- and post-decrement, addition,
subtraction, multiplication, division, results assignable
as integer, printable, settable.

这里&#t; 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

答案 6 :(得分:4)

在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

答案 7 :(得分:3)

在python中没有像C之类的语言中的post / pre递增/递减运算符。

就像数学(-1)*(-1)=(+1)一样,我们可以看到++--是多个符号成倍增加。

例如

---count

解析为

-(-(-count)))

翻译为

-(+count)

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

最后

-count

答案 8 :(得分:1)

TL; DR

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

a += 1

更多细节和陷阱

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

所以可以这么说

a = 1

这在python中的含义是:创建一个类型为int的对象,其值为1,并将名称a绑定到该对象。 object 是值为int的{​​{1}}的实例,并且 name 1引用了它。名称a和它指向的对象是不同的。

现在,您可以说

a

由于a += 1 是不可变的,因此发生的情况如下:

  1. 查找int所引用的对象(它是ID为a的{​​{1}})
  2. 查找对象int(它是0x559239eeb380)的值
  3. 将该值加1(1 + 1 = 2)
  4. 创建具有值0x559239eeb380 new 1对象(对象ID为int
  5. 将名称2绑定到这个新对象
  6. 现在0x559239eeb3a0引用对象a,而原始对象(a)不再用名称0x559239eeb3a0引用。如果没有其他名称引用原始对象,则稍后将对其进行垃圾收集。

尝试一下:

0x559239eeb380

答案 9 :(得分:0)

一个直接的解决方法

#include "examplewindow.h"
#include <iostream>

ExampleWindow::ExampleWindow()
    : m_VBox(Gtk::Orientation::ORIENTATION_VERTICAL),
      m_ButtonBox(Gtk::Orientation::ORIENTATION_VERTICAL),
      m_Button("Show Dialog")
{
  set_title("Test animated gif");
  set_default_size(800, 600);

  add(m_VBox);

  m_VBox.pack_start(m_ButtonBox);
  m_ButtonBox.pack_start(m_Button);
  m_Button.set_hexpand(true);
  m_Button.set_halign(Gtk::Align::ALIGN_CENTER);
  m_Button.set_valign(Gtk::Align::ALIGN_CENTER);
  m_Button.grab_focus();
  m_Button.signal_clicked().connect(sigc::mem_fun(*this, &ExampleWindow::on_button_clicked));

  show_all_children();
}

ExampleWindow::~ExampleWindow()
{
}

void ExampleWindow::on_button_clicked()
{
  Gtk::Dialog m_Dialog;
  m_Dialog.set_transient_for(*this);
  m_Dialog.set_size_request(200, 200);
  m_Dialog.set_decorated(false);
  Gtk::Image imageLoading = Gtk::Image();
  imageLoading.property_pixbuf_animation() = Gdk::PixbufAnimation::create_from_file("gtkmm_logo.gif");
  m_Dialog.get_vbox()->pack_start(imageLoading);
  m_Dialog.show_all();

  m_Dialog.run();

  /******** This, below, never gets executed as run() is blocking the program...********/
  
  // Dummy "long" operation
  for (int i = 0; i <= 2010101010; i++)
  {
    if (i == 2010101010)
      std::cout << "Done" << std::endl;
  }

  m_Dialog.response(Gtk::RESPONSE_ACCEPT);
  m_Dialog.hide();
}

不再打字

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

另外,你可以写 C++ 并完成所有代码,然后搜索/替换“c++”,替换为“c=c+1”。只需确保正则表达式搜索关闭即可。