Python中增量和减量运算符的行为
我注意到一个预先增加/减less运算符可以应用于一个variables(如++count
)。 它编译,但它实际上并没有改变variables的值!
Python中预增/减运算符(++ / – )的行为是什么?
为什么Python偏离了在C / C ++中看到的这些运算符的行为?
++
不是一个运算符。 这是两个+
运营商。 +
运算符是身份运算符,它什么都不做。 (澄清: +
和-
一元运算符只能用于数字,但我认为你不会希望有一个假设的++
运算符来处理string。)
++count
parsing为
+(+count)
转化为
count
你必须使用稍长的+=
运算符来执行你想要做的事情:
count += 1
我怀疑++
和--
操作符被遗漏了一致性和简单性。 我不知道圭多·范·罗索姆(Guido van Rossum)给出这个决定的确切论点,但是我可以想象出几个论点:
- 更简单的parsing。 从技术上讲,parsing
++count
是不明确的,因为它可能是+
,+
,count
(两个一元+
运算符),就像它可能是++
,count
(一个一元++
运算符)一样容易。 这不是一个重要的句法歧义,但它确实存在。 - 更简单的语言。
++
是+= 1
的同义词。 这是一个简短的发明,因为C编译器是愚蠢的,不知道如何优化大多数计算机的inc
指令。 在优化编译器和字节码解释语言的这一天,将运算符添加到语言中以允许程序员优化他们的代码通常是被忽视的,特别是在像Python这样被devise为一致和可读的语言中。 - 混淆的副作用。 在
++
语言的语言中,一个常见的新手错误是把前后recursion/递减运算符之间的差异(优先级和返回值)混合在一起,而Python喜欢消除语言“gotcha”-s。 C中前/后增量的优先级问题非常多,而且非常容易搞砸。
当你想增加或减less,你通常想要在一个整数上做到这一点。 像这样:
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,也不是围绕机器代码的低级包装,而是高级dynamic语言,其中增量没有意义,也不像C中那样必要,例如,每当你有一个循环,你在哪里使用它们。
虽然其他答案只是表明了一个“通常”(即保留原来的数字,如果是原来的数字),那么他们的答案是正确的,但是他们不能解释发生了什么。
准确地说, +x
计算结果是x.__pos__()
和++x
to 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)
在Python中,expression式和语句之间的区别是严格执行的,与Common Lisp,Scheme或Ruby等语言不同。
维基百科
所以通过引入这样的操作符,你会打破expression式/语句拆分。
出于同样的原因,你不能写
if x = 0: y = 1
正如你可以在其他一些语言中保留这种区别一样。
Python没有这些运算符,但是如果你真的需要它们,你可以编写一个具有相同function的函数。
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
在一个函数内部,如果你想改变局部variables,你必须添加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()
也可以使用这些function:
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。
是的,我错过了++和 – function。 几百万行的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作为types计数器:
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',cv) # and getting 42. print s
…或者更详细地说,明确地说我们实际上想要发生的事情,尽pipe用实际的forms反过来用冗长(用cv
代替)。
c = counter(42) s = '%s: %d' % ('Expecting 42',c.__getitem__()) # and getting 42. print s