为什么Python中没有++和-运算符?


Answers:


443

不是因为它没有道理;而是因为它没有意义。最好将“ x ++”定义为“ x + = 1,求出x的先前绑定”。

如果您想知道最初的原因,则必须要么浏览旧的Python邮件列表,要么询问那里的某个人(例如Guido),但是在事实成立之后就很容易找到理由了:

与其他语言一样,不需要简单的增量和减量。您不会for(int i = 0; i < 10; ++i)经常用Python 编写东西。相反,你做类似的事情for i in range(0, 10)

由于几乎不需要它,因此没有太多理由为其提供特殊的语法。当您确实需要增加时,+=通常就可以了。

这不是是否有意义,还是可以做到的决定。这是一个好处是否值得添加到该语言的核心语法中的问题。请记住,这是四个运算符-postinc,postdec,preinc,predec,并且每个运算符都需要具有自己的类重载;他们都需要指定和测试;它将在语言中添加操作码(暗示更大,因此更慢的VM引擎);每个支持逻辑增量的类都需要实现它们(在+=和之上-=)。

+=和都是多余的-=,因此将成为净亏损。


98
使用诸如array [i ++]之类的东西通常很有用,而使用+ = /-=并不是一件容易的事。
特纳·海斯

102
@thayes:在Python中这不是常见的模式。
格伦·梅纳德

9
@thayes因为这将在循环内,所以您最好i直接循环-如果您确实需要它,而不能仅仅使用它array.append()
Tobias Kienzler 2013年

31
我看到更大的关注是可读性和可预测性。早在我的C日子里,我看到了足够多的虫子从误解而产生约之间的区别i++++i...
查尔斯·达菲

5
事后证明:在我从事的项目中,我遇到(比他们一生中的任何人都要多的事情)大量的C ++代码,这些代码存在问题++--以导致未定义或未指定的方式使用行为。它们使编写复杂的,难以正确解析的代码成为可能。
Brian Vandenberg 2015年

84

我写的这个原始答案是关于计算机民俗的一个神话:被丹尼斯·里奇(Dennis Ritchie)认为是“历史上不可能的”,正如ACM通讯编辑在2012年7月致函doi:10.1145 / 2209249.2209251


C增/减运算符是在C编译器不是很聪明的时候发明的,作者希望能够指定使用机器语言运算符的直接意图,从而节省了编译器的几个周期,可能会做一个

load memory
load 1
add
store memory

代替

inc memory 

PDP-11甚至支持分别对应于*++p和的“自动递增”和“延迟自动递增”指令*p++。如果非常好奇,请参阅手册第5.3节。

由于编译器足够聪明,可以处理C语法中内置的高级优化技巧,因此它们现在只是语法上的便利。

Python没有技巧来向汇编器传达意图,因为它不使用汇编器。


4
Javascript具有++。我认为这不是“将意图传达给汇编程序的技巧”。另外,Python确实具有字节码。因此,我认为原因是其他原因。
内森·戴维斯

13
这种“向编译器提供提示”的业务确实是一个神话。坦率地说,这是对任何语言的愚蠢补充,它违反了以下两个规则:1.您无需编写代码即可让计算机阅读,而是编写代码供其他工程师阅读。 还有2.您不是要让合格的工程师阅读,而是要让合格的工程师阅读,因为他们在凌晨3点精疲力尽,并喝咖啡因。

3
@ tgm1024为了公平起见,当以每秒10–30个字符的半双工电传打字机进行编码时,请进行编码,以便可以在下周之前键入。
msw 2015年

4
@ tgm1024 Unix和C在PDP-11上进行了大量的初始开发,这些PDP-11使用了极其慢的电传打字机来进行用户通信。尽管您完全没错,今天为机器编写的代码大多数都是愚蠢的,但那时的瓶颈是人机界面。很难想象如果您从未如此缓慢地工作。
msw 2015年

65

我一直以为这与python禅的这一行有关:

应该有一种(最好只有一种)明显的方式来做到这一点。

x ++和x + = 1做完全相同的事情,因此没有理由同时使用两者。


10
one--是零吗?
Andre Holzner 2013年

25
one--在句子中为1,但之后立即为零。因此,这种“ koan”也暗示增量/减量运算符不是显而易见的。
维克多·K

14
@EralpB如果删除+ =,则无法执行x + = 10之类的操作。+ =是++的一种更常见的情况
Rory 2014年

8
另外:“显式优于隐式”。
2014年

2
绝对不一样,因为x + = 1不是表达式-它是语句-它不求任何值。你不能做这样的事情:'row [col ++] = a; row [col ++] = b'。更不用说c ++具有的pre-inc和post-inc内容。
Uri 2014年

38

当然,我们可以说“圭多岛就是这样决定的”,但我认为问题实际上是做出该决定的原因。我认为有以下几个原因:

  • 它将陈述和表达式混合在一起,这不是一个好习惯。参见http://norvig.com/python-iaq.html
  • 它通常会鼓励人们编写可读性较低的代码
  • 如前所述,语言实现的额外复杂性在Python中是不必要的

12
终于有人高兴地提到了陈述与表达方面。在C中,赋值是一个表达式,因此它是++运算符。在Python中,赋值是一条语句,因此,如果它具有++,则它也可能也需要是一条赋值语句(甚至用处不大或不需要)。
martineau 2010年

同意-如果它们是语句,那么至少谈论后运算符和前置运算符之间的区别将变得毫无意义。
Crowman

17

因为在Python中,整数是不可变的(int的+ =实际上返回了一个不同的对象)。

同样,使用++ /-时,您需要担心增量前后的递增和递减,并且只需要再写一次按键即可x+=1。换句话说,它避免了潜在的混乱,但付出的代价却很小。


int在C中也是不可变的。如果您不这么认为,请尝试让您的C编译器生成用于42++...的代码…… 在某些旧的Fortran编译器中(实际上我已经读过)实际上可能这样的(修改文字常量):将来的所有使用那么,该程序运行中的文字的确会具有不同的值。调试愉快!
Lutz Prechelt 2015年

10
对。42是字面常量。常量是(或至少应该是)不变的。这并不意味着C int通常是不可变的。intC中的in只是指定内存中的位置。而且那个地方的位子非常可变。例如,您可以创建的引用int并更改该引用的引用。此更改在int对该位置的所有引用(包括原始变量)中均可见。对于Python整数对象,这并不适用。
内森·戴维斯

x + = 1最终不会成为inc mem。Python执行函数调用以将1加到变量中;这是可悲的。
PalaDolphin

12

明晰!

Python有很多关于清晰度--a的知识,除非他/她学习了具有这种构造的语言,否则任何程序员都不可能正确地猜测的含义。

Python还有很多关于避免引发错误的构造的知识,并且++已知运算符是缺陷的丰富来源。这两个原因足以在Python中没有这些运算符。

Python使用缩进来标记块而不是诸如某种形式的开始/结束方括号或强制结束标记之类的句法手段的决定很大程度上基于相同的考虑。

作为说明,请看一下有关在2005年向Python中引入条件运算符(在C:中cond ? resultif : resultelse)的讨论。至少请阅读该讨论第一条消息决策消息(之前有相同主题的多个先驱)。

琐事: 其中经常提到的PEP是“ Python扩展建议” PEP 308。LC表示列表理解,GE表示生成器表达式(不要担心,如果它们使您感到困惑,它们不是Python的少数复杂地方)。


10

我对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现在指向另一个对象,为bc。因为您已经使用a = a + 1它,所以它很明显。换句话说,您将另一个对象完全分配给label a。想象一下,您可以编写a++它,这表明您没有分配给变量a新对象,而是增加了旧对象的数量。所有这些东西都是恕我直言,以尽量减少混乱。为了更好地理解,请参见python变量如何工作:

在Python中,为什么函数可以修改调用者认为的某些参数,而不能修改其他参数?

Python是按值调用还是按引用调用?都不行

Python是按值传递还是按引用传递?

Python是按引用传递还是按值传递?

Python:如何通过引用传递变量?

了解Python变量和内存管理

在python中模拟按值传递行为

Python函数通过引用调用

像Pythonista一样的代码:惯用的Python


9

它就是这样设计的。递增和递减运算符只是的快捷方式x = x + 1。Python通常采用了一种设计策略,该策略减少了执行操作的替代方法的数量。 增量分配是Python中最接近递增/递减运算符的东西,直到Python 2.0才添加。


3
是的,例如,您可以替换return a[i++]return a[i=i+1]
路易斯·德·索萨

7

我是python的新手,但我怀疑原因是由于该语言中的可变对象和不可变对象之间的强调。现在,我知道x ++可以很容易地解释为x = x + 1,但是它看起来就像您就地递增一个不可变的对象一样。

只是我的猜测/感觉/预感。


在这方面,这两个变量在可变对象上也比x++更加接近。x += 1x = x + 1
glglgl 2012年

4

首先,Python仅受C间接影响。它在很大程度上受到影响ABC,这显然不具备这些运营商,所以它不应该有任何巨大的惊喜不会找到它们在Python两种。

其次,正如其他人所说,递增和递减由支持+=-=了。

第三,对++and --运算符集的完全支持通常包括同时支持它们的前缀和后缀版本。在C和C ++中,这可能导致各种“可爱”的构造(在我看来)与Python所包含的简单性和直截了当的精神背道而驰。

例如,尽管C语句while(*t++ = *s++);对于有经验的程序员而言似乎简单而优雅,但对于学习它的人来说,却绝非简单。混合使用前缀和后缀增量和减量,甚至许多专业人士也必须停下来思考一下。


3

我认为这源于Python的信条,即“明确胜于隐含”。


好吧,您没有在Python中显式地编写“ begin”和“ end”语句,对吗?尽管我同意这一说法,但我认为这是有界限的。尽管我们可以就这些界限争论不休,但我认为我们都可以同意,有一条界限是不切实际的。而且,由于对该决定有很多意见和理由,所以我认为这不是一个明确的选择。至少,在明确说明的地方,我找不到来源
Hasan Ammori

3

这可能是因为@GlennMaynard正在将问题与其他语言进行比较,但是在Python中,您是以python方式进行操作的。这不是一个“为什么”的问题。在那里,您可以使用达到相同的效果x+=。在《 Python的禅宗》中,给出了:“只有一种解决问题的方法。” 多种选择在艺术上(表达自由)很棒,但在工程上却很​​糟糕。


2

++运算符的类别是具有副作用的表达式。这是Python中通常找不到的东西。

出于同样的原因,赋值不是Python中的表达式,因此防止了通用 if (a = f(...)) { /* using a here */ }用法。

最后,我怀疑操作符与Python的参考语义不是很一致。请记住,Python没有具有C / C ++已知语义的变量(或指针)。


1
没有什么可以阻止在测试/表达式/列表理解中调用具有副作用的函数:f(a)哪里a是列表,一些不可变的对象。
让·弗朗索瓦·法布尔

1

也许更好的问题是问为什么这些运算符存在于C中。K&R调用增量和减量运算符为“异常”(第2.8页第2.8节)。导言称它们“更简洁,通常更高效”。我怀疑这些操作总是在指针操作中出现的事实也影响了它们的引入。在Python中,可能已经决定尝试优化增量没有任何意义(事实上,我只是在C中进行了测试,而且似乎gcc生成的程序集在两种情况下都使用addl而不是incl),并且没有指针算法;因此,它本来只是另一种实现方式,而我们知道Python不愿这样做。


1

据我了解,所以您不会认为内存中的值已更改。在c中,当执行x ++时,内存中x的值会更改。但是在python中,所有数字都是不可变的,因此x指向的地址仍然具有x而不是x + 1。当您编写x ++时,您可能会认为x发生了改变,实际上是x引用更改为x + 1存储在内存中的位置,或者如果doe不存在,则重新创建该位置。


1
那么,这++与什么不同+= 1呢?
2014年

1

要在该页面上完成已经很好的答案:

假设我们决定这样做,在前缀(++i)处打乱一元+和-运算符。

今天,以++--做任何前缀都没有,因为它使一元加号运算符两次(不执行任何操作)或一元减号两次(两次:取消自身)成为可能

>>> i=12
>>> ++i
12
>>> --i
12

这样可能会破坏这种逻辑。


1

其他答案描述了为什么迭代器不需要它,但是有时在分配以增加内联变量时它很有用,您可以使用元组和多重分配来达到相同的效果:

b = ++a 变成:

a,b = (a+1,)*2

b = a++成为:

a,b = a+1, a

Python的3.8引入了分配:=操作,使我们能够实现foo(++a)

foo(a:=a+1)

foo(a++) 虽然仍然难以捉摸。


2
:=分配是一个耻辱
nehem

0

我认为这涉及对象的可变性和不可变性的概念。2,3,4,5在python中是不可变的。请参考下图。2具有固定的ID,直到此python进程为止。

常量和变量的ID

x ++本质上意味着像C的就地增量。在C中,x ++执行就地增量。因此,x = 3,并且x ++会将内存中的3增加到4,这与python中内存中仍然存在3的情况不同。

因此,在python中,您无需在内存中重新创建值。这可能会导致性能优化。

这是基于预感的答案。


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()) :
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.