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

2024-12-19 09:24:00
admin
原创
129
摘要:问题描述:如何使用预增量/减量运算符(++,--),就像在 C++ 中一样?为什么++count运行了,但是变量的值却没有改变呢?解决方案 1:++不是运算符。它是两个+运算符。+运算符是恒等运算符,它不执行任何操作。(澄清:+和-一元运算符仅适用于数字,但我认为您不会期望假设的++运算符适用于字符串。)++...

问题描述:

如何使用预增量/减量运算符(++--),就像在 C++ 中一样?

为什么++count运行了,但是变量的值却没有改变呢?


解决方案 1:

++不是运算符。它是两个+运算符。+运算符是恒等运算符,它不执行任何操作。(澄清:+-一元运算符仅适用于数字,但我认为您不会期望假设的++运算符适用于字符串。)

++count

解析为

+(+count)

这意味着

count

您必须使用稍长的+=操作符来完成您想做的事情:

count += 1

我怀疑++and--运算符被省略是为了一致性和简单性。我不知道 Guido van Rossum 给出的确切理由,但我可以想象几个理由:

  • 更简单的解析。从技术上讲,解析++count是模棱两可的,因为它可以是+, +, count(两个一元+运算符),也可以是++, count(一个一元++运算符)。这不是一个重大的句法歧义,但它确实存在。

  • 更简单的语言。++只不过是的同义词+= 1。它之所以被发明出来,是因为 C 编译器很笨,不知道如何优化大多数计算机的a += 1指令inc。在优化编译器和字节码解释语言的今天,在语言中添加运算符以允许程序员优化他们的代码通常会被反对,尤其是在像 Python 这样旨在保持一致性和可读性的语言中。

  • 令人困惑的副作用。在带有++运算符的语言中,新手常犯的一个错误是混淆前置和后置增量/减量运算符之间的差异(无论是优先级还是返回值),而 Python 喜欢消除语言“陷阱”。C中前置/后置增量的优先级问题相当棘手,而且极易搞砸。

解决方案 2:

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

这将重新分配bb+1。这不是增量运算符,因为它不会增加b,而是重新分配它。

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

解决方案 3:

尽管其他答案在显示了 me+通常所做的事情(即,如果数字为 1,则保留其原样)方面是正确的,但它们并不完整,因为它们没有解释发生了什么。

确切地说,+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:

在 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

解决方案 5:

总结

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

a += 1

更多细节和问题

但这里要小心。如果你以前用过 C,那么在 Python 中这一点就不同了。Python 没有 C 中意义上的“变量”,而是使用名称对象,并且在 Python 中,int它们是不可变的。

假设你这样做

a = 1

在 Python 中,这意味着:创建一个int具有值类型的对象1并将名称绑定a到它。该对象int是具有值的实例1,名称引用 a它。名称a和它引用的对象是不同的。

现在假设你

a += 1

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

  1. 查找a引用的对象(它是一个int带有 id 的对象0x559239eeb380

  2. 查找对象的值0x559239eeb380(它是1

  3. 将这个值加 1(1 + 1 = 2)

  4. 创建一个具有值的 对象(它有对象 id )int`2`0x559239eeb3a0

  5. 将名称重新绑定a到这个新对象

  6. 现在a引用 object 0x559239eeb3a0,而原始对象 ( 0x559239eeb380) 不再由名称 引用a。如果没有其他名称引用原始对象,它将在稍后被垃圾回收。

自己尝试一下:

a = 1
print(hex(id(a)))
a += 1
print(hex(id(a)))

解决方案 6:

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

解决方案 7:

在 Python 中,与 Common Lisp、Scheme 或 Ruby 等语言相比,表达式和语句之间的区分是严格强制的。

维基百科

因此,通过引入这样的运算符,您可以打破表达式/语句的分裂。

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

if x = 0:
  y = 1

在其他一些没有保留这种区别的语言中也是如此。

解决方案 8:

是的,我也怀念 ++ 和 -- 功能。几百万行 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

解决方案 9:

像 C 语言那样,Python 中没有后/前增量/减量运算符。

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

例如

---count

解析为

-(-(-count)))

这意味着

-(+count)

-因为,符号与-符号相乘是+

最后,

-count

解决方案 10:

扩展亨利的回答,我实验性地实现了一个语法糖库a++:hdytto。

使用方法很简单。从 PyPI 安装后,放置sitecustomize.py

from hdytto import register_hdytto
register_hdytto()

在你的项目目录中。然后,make main.py

# coding: hdytto

a = 5
print(a++)
print(++a)
b = 10 - --a
print(b--)

并运行它PYTHONPATH=. python main.py。输出将是

5
7
4

在解码脚本文件时,hdytto 替换a++as ,因此它可以起作用。((a:=a+1)-1)

解决方案 11:

一个直接的解决方法

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

不再打字

 c = c + 1

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

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用