当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《Python中递增和递减运算符的行为》经验,为你挑选了6个好方法。

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

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

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



1> Chris Lutz..:

++不是运营商.这是两个+运营商.该+运营商的身份运营,这什么都不做.(澄清:+-一元运算符只能处理数字,但我认为你不会指望一个假设的++运算符来处理字符串.)

++count

解析为

+(+count)

这转化为

count

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

count += 1

我怀疑++--操作员是因为一致性和简单性而被排除在外.我不知道Guido van Rossum给出的确切论据,但我可以想象一些论点:

更简单的解析.从技术上讲,解析++count是模糊的,因为它可能是+,+,count(两个一元+经营者)一样容易,因为它可能是++,count(一个一元++运算符).这不是一个重要的句法歧义,但确实存在.

更简单的语言.++只不过是一个同义词+= 1.这是一个简化发明,因为C编译器是愚蠢的,不知道如何优化大多数计算机a += 1inc指令.在优化编译器和字节码解释语言的今天,向一种语言添加运算符以允许程序员优化其代码通常是不受欢迎的,特别是在像Python这样设计为一致且可读的语言中.

令人困惑的副作用.使用++运算符的语言中的一个常见新手错误是混合了增量前/减值运算符之间的差异(优先级和返回值),Python喜欢消除语言"gotcha"-s.该优先问题的前/后加用C是相当毛,和令人难以置信的容易陷入困境.


另外,请注意,在Python中,+ =和friends不是可以在表达式中使用的运算符.相反,在Python中,它们被定义为"扩充赋值语句"的一部分.这与Python中的语言设计决策一致,不允许赋值("=")作为任意表达式中的运算符,这与C中可以做的不同.请参阅http://docs.python.org/reference/simple_stmts.html #增强赋值语句
我打赌解析器简化.请注意[PEP 3099](http://www.python.org/dev/peps/pep-3099/)中的一项,"Python 3000中不会改变的事情":"解析器不会比LL(1).简单比复杂更好.这个想法扩展到解析器.将Python的语法限制为LL(1)解析器是一种祝福,而不是诅咒.它让我们戴上手铐,防止我们过火和结束与其他一些未命名的动态语言一样,时髦的语法规则,如Perl." 我没有看到如何在不破坏LL(1)的情况下消除歧义"+ +"和"++".
一元`+`运算符有用.对于decimal.Decimal对象,它将舍入为当前精度.
"+运算符是"身份"运算符,它什么都不做." 仅适用于数字类型; 对于其他类型,默认情况下是一个错误.
说"++"只不过是"+ = 1"的同义词,这是不正确的.有++的预增量和后增量变体,所以它显然不是一回事.不过,我同意你的其他观点.
对.但话说回来,`++`和` - `应该只适用于数字类型.
这个答案是正确的,所以+1,但这只是Python的另一个烦恼,让我真的不喜欢它.`strip()`而不是`trim()`就像其他语言一样,没有开关,有些OO函数,有些没有([`len(s)`](http://stackoverflow.com/a/237150/156755) vs [`s.lower()`](http://stackoverflow.com/a/6797990/156755)).非常令人沮丧和不一致的语言
'Python喜欢消除语言"gotcha"-s' - 这不是一种语言问题吗?
各自为自己.我一直使用++和 - 并且最终不会弄乱任何东西.Python是一种很好的语言,但是它有很多缺陷,我发现社区对各种语言结构都很虔诚.它是一个工具,而不是蒙娜丽莎.让我们完成工作并继续下一个任务......这很可能是用R,或Matlab或C++编写的
@CivFan然后a)`__len __()`不是真正的私有,它只是公开但通过间接调用b)我们现在需要知道当对象可能只有一个时调用私有方法的所有全局方法length()`方法,如果需要可以覆盖.

2> Lennart Rege..:

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

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

哪个会重新分配bb+1.这不是增量运算符,因为它不会递增b,而是重新分配它.

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


这个例子是错误的(你可能会混淆不可变性与身份) - 由于某些vm优化使用相同的对象进行数字直到255(或类似的东西),它们具有相同的id.例如(更大的数字):>>> a = 1231231231231 >>> b = 1231231231231 >>> id(a),id(b)(32171144,32171168)
不可变性主张是虚假的.从概念上讲,`i ++`意味着将`i + 1`分配给*变量*`i`.`i = 5; i ++`意味着将`6`分配给`i`,而不是修改`i`指向的`int`对象.也就是说,它并不意味着[增加"5"的值](http://mathworld.wolfram.com/SufficientlyLarge.html)!
@LennartRegebro:在C++和Java中,`i ++'只对lvalues运行.如果它打算增加`i`指向的对象,则不需要这种限制.
我觉得这个答案令人困惑。为什么要假设++意味着+ = 1的简写呢?这正是C语言的含义(假设未使用返回值)。您似乎想出了一些其他含义。
@Mechanical snail:在这种情况下,它根本不是增量运算符.然后+ =运算符更清晰,更明确,更灵活,无论如何都会做同样的事情.
@LennartRegebro - 是的; 我现在看到我的困惑只是"增量"的定义.我觉得"增量"的定义与语言的等义语义联系在一起,这意味着如果`f(x)== x + 1,任何没有副作用的函数`f`都可以称为"增量". `,始终适用于所有数值`x`.其他人显然不这么看,我将来会记住这一点.无论如何,+1都是一个发人深省的答案.PS - Clojure的增量运算符比`++`更明确:它是`(inc value)`.
在这种情况下,不变性确实无关紧要.考虑一下:Clojure有一个内置的增量运算符,默认情况下所有数据结构都是不可变的.虽然这是真的,你得到一个新值的新引用,这主要与`++`与`+ = 1`的纯语法选择正交.
这种解释尽管似乎并不合理:解释器可以使“ ++ a”与“ a = a + 1”具有完全相同的效果,并且使“ a”指向ID为6。我认为真正的问题在于+是一元运算符,而++ a可能意味着`increment a`或`+(+ a)`,这对于python解析器来说是无法区分的。
这个解释(不可变的)也出现在我昨天阅读的Python书中.
正如Don Hatch所说的那样,在其他语言中(例如Java),整数也是不可变的,并且执行++ 1或* ++只是将变量指向的值更改为大于1的值。它当前指向。两个运算符都不会修改变量所指向的值。对于为什么没有添加`++`运算符,这个答案确实没有给出充分的理由。

3> glglgl..:

虽然其他答案是正确的,只要它们表明+通常只是做什么(即,保留数字,如果它是一个),它们是不完整的,因为它们不解释会发生什么.

确切的说,+x计算结果为x.__pos__()++xx.__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> Piotr Dabkow..:

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> Vitalii Fedo..:

在Python中,与Common Lisp,Scheme或Ruby等语言相比,严格执行表达式和语句之间的区别.

维基百科

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

出于同样的原因你不能写

if x = 0:
  y = 1

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



6> fyngyrz..:

是的,我也错过了++和-功能。几百万行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 #  323

...这将使c保持为类型计数器:

c = counter(100)
c.set(c + 223)
print type(c),c #  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

推荐阅读
地之南_816
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有