为什么Python中没有++
和--
运算符?
答案 0 :(得分:417)
这不是因为它没有意义;将“x ++”定义为“x + = 1,对x的先前绑定进行评估”是完全合理的。
如果你想了解最初的原因,你将不得不浏览旧的Python邮件列表或询问那里的人(例如Guido),但事实证明这很容易证明:
不需要像其他语言那样简单的增量和减量。你不经常在Python中写for(int i = 0; i < 10; ++i)
之类的东西;相反,你会做for i in range(0, 10)
之类的事情。
由于它几乎不需要经常使用,因此没有理由给它自己特殊的语法;当你需要增加时,+=
通常就好了。
这不是决定它是否有意义,或者它是否可以完成 - 确实如此,它可以。这是一个问题,是否有益于增加语言的核心语法。请记住,这是四个运算符 - postinc,postdec,preinc,predec,并且每个运算符都需要有自己的类重载;他们都需要被指定和测试;它会在语言中添加操作码(暗示一个更大的,因此更慢的VM引擎);每个支持逻辑增量的类都需要实现它们(在+=
和-=
之上)。
这对于+=
和-=
都是多余的,因此会成为净损失。
答案 1 :(得分:81)
我写的这个原始答案是来自计算民间传说的神话:Dennis Ritchie揭穿的“历史上不可能”,正如 ACM通讯编辑所写的那样< / em> 2012年7月doi:10.1145/2209249.2209251
C增量/减量运算符是在C编译器不是非常智能的时候发明的,并且作者希望能够指定应该使用机器语言运算符的直接意图,这样可以节省一些周期可以执行
的编译器load memory
load 1
add
store memory
而不是
inc memory
,PDP-11甚至支持分别对应*++p
和*p++
的“自动增量”和“自动递增延迟”指令。如果可怕的话,请参见the manual的第5.3节。
由于编译器足够聪明,可以处理C语法中内置的高级优化技巧,因此它们现在只是语法上的便利。
Python没有把意图传达给汇编程序的技巧,因为它没有使用它。
答案 2 :(得分:59)
我一直认为它与python的这一行有关:
应该有一种 - 最好只有一种 - 显而易见的方法。
x ++和x + = 1完全相同,所以没有理由同时使用两者。
答案 3 :(得分:36)
当然,我们可以说“Guido刚刚决定了这种方式”,但我认为这个问题的确是这个问题的原因。我认为有几个原因:
答案 4 :(得分:16)
因为在Python中,整数是不可变的(int的+ =实际上返回一个不同的对象)。
此外,使用++ / - 您需要担心前后增量/减量,并且只需要再写一次键击x+=1
。换句话说,它避免了潜在的混淆,但却以非常小的收益为代价。
答案 5 :(得分:12)
Python非常关注清晰度,并且没有程序员可能正确地猜测--a
的含义,除非他/她已经学会了具有该构造的语言。
Python也非常关注避免引发错误的构造,并且已知++
运算符是丰富的缺陷来源。
这两个原因足以让Python中没有这些运算符。
决定Python使用缩进来标记块 比句法手段,如某种形式的开始/结束包围 或强制性最终标记主要基于相同的考虑因素。
为了说明,请参阅2005年在Python中引入条件运算符(在C:cond ? resultif : resultelse
中)的讨论。
至少阅读first message的decision message和discussion(之前有相同主题的几个前兆)。
<强>花絮:强> 其中经常提到的PEP是&#34; Python扩展提案&#34; PEP 308。 LC表示list comprehension,GE表示generator expression(如果那些让您感到困惑,不要担心,它们不是Python的少数复杂点)。
答案 6 :(得分:9)
它就是这样设计的。递增和递减运算符只是x = x + 1
的快捷方式。 Python通常采用一种设计策略,减少了执行操作的替代方法的数量。 Augmented assignment是在Python中增加/减少运算符的最接近的东西,它们甚至在Python 2.0之前都没有添加。
答案 7 :(得分:8)
我对python很新,但我怀疑原因是因为语言中的可变对象和不可变对象之间的重点。现在,我知道x ++可以很容易地被解释为x = x + 1,但它看起来就像你正在增加就地一个可能是不可变的对象。
只是我的猜测/感觉/预感。
答案 8 :(得分:8)
我对python没有++
运算符的理解如下:当你在python a=b=c=1
中写这个时,你会得到三个指向同一个对象的变量(标签)(值为1)。您可以使用id函数验证这一点,该函数将返回一个对象内存地址:
In [19]: id(a)
Out[19]: 34019256
In [20]: id(b)
Out[20]: 34019256
In [21]: id(c)
Out[21]: 34019256
所有三个变量(标签)都指向同一个对象。现在增加一个变量并查看它如何影响内存地址:
In [22] a = a + 1
In [23]: id(a)
Out[23]: 34019232
In [24]: id(b)
Out[24]: 34019256
In [25]: id(c)
Out[25]: 34019256
您可以看到变量a
现在指向另一个对象作为变量b
和c
。因为您已使用a = a + 1
,所以明确说明了这一点。换句话说,您将另一个对象分配给标签a
。想象一下,你可以写a++
它会建议你没有分配给变量a
新对象但是增加旧对象。所有这些东西都是恕我直言,以尽量减少混乱。为了更好地理解python变量如何工作:
In Python, why can a function modify some arguments as perceived by the caller, but not others?
Is Python call-by-value or call-by-reference? Neither.
Does Python pass by value, or by reference?
Is Python pass-by-reference or pass-by-value?
Python: How do I pass a variable by reference?
Understanding Python variables and Memory Management
Emulating pass-by-value behaviour in python
答案 9 :(得分:4)
我认为它源于Python信条“明确胜过隐性”。
答案 10 :(得分:4)
首先,Python只受C的间接影响;它受ABC的影响很大,apparently does not have these operators,所以不要在Python中找到它们也不会有任何意外。
其次,正如其他人所说,+=
和-=
已经支持增量和减量。
第三,对++
和--
运算符集的完全支持通常包括支持它们的前缀和后缀版本。在C和C ++中,这可能会导致各种“可爱的”结构(对我而言)违背Python所包含的简单性和直接性的精神。
例如,虽然C语句while(*t++ = *s++);
对于有经验的程序员来说看似简单而优雅,但对于学习它的人来说,这简直就是一件简单的事。抛出前缀和后缀增量和减量的混合,甚至许多专业人员将不得不停下来思考一下。
答案 11 :(得分:3)
这可能是因为@GlennMaynard正在考虑与其他语言相比较的问题,但在Python中,你以python的方式做事。这不是一个'为什么'的问题。它就在那里你可以用x+=
做同样的事情。在The Zen of Python中,给出了:“应该只有一种方法来解决问题。”艺术(表达自由)有多种选择,但在工程方面却很糟糕。
答案 12 :(得分:2)
因为我理解它所以你不会认为内存中的值会改变。 在c中,当你执行x ++时,内存中x的值会发生变化。 但是在python中,所有数字都是不可变的,因此x指向的地址仍然是x而不是x + 1。当您编写x ++时,您会认为x更改实际发生的是x refrence更改为存储x + 1的内存中的位置,或者如果doe不存在则重新创建此位置。
答案 13 :(得分:2)
++
类运算符是带副作用的表达式。这是Python中通常没有的东西。
出于同样的原因,赋值不是Python中的表达式,因此阻止了常见的if (a = f(...)) { /* using a here */ }
习语。
最后我怀疑运算符与Pythons引用语义不一致。请记住,Python没有带有C / C ++已知语义的变量(或指针)。
答案 14 :(得分:1)
也许更好的问题是问为什么这些运算符存在于C. K&amp; R调用递增和递减运算符'异常'(第2.8节第46页)。引言称它们“更简洁,通常更有效”。我怀疑这些操作总是出现在指针操作中的事实也在他们的介绍中发挥了作用。 在Python中,可能已经决定尝试优化增量是没有意义的(事实上我只是在C中进行了测试,似乎gcc生成的程序集在两种情况下都使用addl而不是incl)并且没有指针算术;所以它本来就是另一种方法,我们知道Python不喜欢它。
答案 15 :(得分:0)
要在该页面上完成已经很好的答案:
假设我们决定这样做,前缀(++i
)会破坏一元+和 - 运算符。
今天,++
或--
的前缀不起任何作用,因为它可以使一元加运算符两次(不执行任何操作)或一元减去两次(两次:取消自身)
>>> i=12
>>> ++i
12
>>> --i
12
所以这可能会打破这种逻辑。
答案 16 :(得分:0)
我认为这涉及对象的可变性和不可变性的概念。 2,3,4,5在python中是不可变的。请参考下图。 2具有固定的ID,直到此python进程为止。
x ++本质上是指就位增量,如C。在C中,x ++执行就位增量。因此,x = 3,并且x ++会将内存中的3增加到4,这与python中内存中仍然存在3的情况不同。
因此在python中,您无需在内存中重新创建值。这可能会导致性能优化。
这是基于直觉的答案。
答案 17 :(得分:0)
我知道这是一个旧线程,但是没有涵盖++ i的最常见用例,即在没有提供索引的情况下手动设置索引集。这就是为什么python提供enumerate()
的原因示例:在任何给定的语言中,当您使用诸如foreach之类的结构遍历一个集合时-就本示例而言,我们甚至会说它是无序集合,并且您需要一个唯一的索引来区分所有内容,说
i = 0
stuff = {'a': 'b', 'c': 'd', 'e': 'f'}
uniquestuff = {}
for key, val in stuff.items() :
uniquestuff[key] = '{0}{1}'.format(val, i)
i += 1
在这种情况下,python提供了一个枚举方法,例如
for i, (key, val) in enumerate(stuff.items()) :
答案 18 :(得分:0)
其他答案描述了为什么迭代器不需要它,但是有时在分配以增加内联变量时它很有用,您可以使用元组和多重分配来达到相同的效果:
b = ++a
变为:
a,b = (a+1,a+1)
和b = a++
变为:
a,b = a+1, a
Python 3.8引入了赋值:=
运算符,使我们可以通过
foo(++a)
foo(a:=a+1)
foo(a++)
仍然难以捉摸。
答案 19 :(得分:0)
除了这里的其他优秀答案之外,++
和 --
也因未定义的行为而臭名昭著。例如,这段代码会发生什么?
foo[bar] = bar++;
它看起来很无辜,但它是错误的 C(和 C++),因为您不知道第一个 bar
是否会增加。一个编译器可能会以一种方式这样做,另一个可能会以另一种方式这样做,而第三个可能会让恶魔从你的鼻子里飞出来。一切都将完全符合 C 和 C++ 标准。
未定义的行为在 C 和 C++ 中被视为一种必要的邪恶,但在 Python 中,它只是邪恶的,并尽可能避免。
答案 20 :(得分:-3)
++运算符与+ =运算符不完全相同。 实际上两者的结果是相同的但是使用有一些区别。 例如,您可以在三元条件,for循环等中使用++运算符,但不能使用+ =。 在底部,我们感到需要++和 - ,出于这个原因。