C/C++/Java等等语言中,整型变量的自增或自减操作是标配,它们又可分为前缀操作(++i和--i)与后缀操作(i++和i--),彼此存在着一些细微差别,各有不同的用途。
这些语言的使用者在接触Python时,可能会疑惑为什么它不提供++或--的操作呢?
Python中虽然可能出现++i这种前缀形式的写法,但是它并没有“++”自增操作符,此处只是两个“+”(正数符号)的叠加而已,至于后缀形式的“++”,则完全不支持(SyntaxError:invalidsyntax)。
本期“Python为什么”栏目,我们将会从两个主要的角度来回答:Python为什么不支持i++自增语法?(PS:此处自增指代“自增和自减”,下同)
首先,Python当然可以实现自增效果,即写成i+=1或者i=i+1,这在其它语言中也是通用的。
虽然Python在底层用了不同的魔术方法(__add__和__iadd__)来完成计算,但表面上的效果完全相同。
所以,我们的问题可以转化成:为什么上面的两种写法会胜过i++,成为Python的最终选择呢?
1、Python的整数是不可变类型
当我们定义i=1000时,不同语言会作出不同的处理:
C之类的语言(写法inti=1000)会申请一块内存空间,并给它“绑定”一个固定的名称i,同时写入一个可变的值1000。在这里,i的地址以及类型是固定的,而值是可变的(在一定的表示范围内)
Python(写法i=1000)也会申请一块内存空间,但是它会“绑定”给数字1000,即这个1000的地址以及类型是固定的(immutable),至于i,只是一个名称标签贴在1000上,自身没有固定的地址和类型
所以当我们令i“自增”时(i=i+1),它们的处理是不同的:
C之类的语言先找到i的地址上存的数值,然后令它加1,操作后新的数值就取代了旧的数值
Python的操作过程是把i指向的数字加1,然后把结果绑定到新申请的一块内存空间,再把名称标签i“贴”到新的数字上。新旧数字可以同时存在,不是取代关系
打一个不太恰当的比方:C中的i就像一个宿主,数字1000寄生在它上面;而Python中的1000像个宿主,名称i寄生在它上面。C中的i与Python中的1000,它们则寄生在底层的内存空间上……
还可以这样理解:C中的变量i是一等公民,数字1000是它的一个可变的属性;Python中的数字1000是一等公民,名称i是它的一个可变的属性。
有了以上的铺垫,我们再来看看i++,不难发现:
C之类的语言,i++可以表示i的数字属性的增加,它不会开辟新的内存空间,也不会产生新的一等公民。
Python之类的语言,i++如果是对其名称属性的操作,那样就没有意义了(总不能按字母表顺序,把i变成j吧);如果理解成对数字本体的操作,那么情况就会变得复杂:它会产生新的一等公民1001,因此需要给它分配一个内存地址,此时若占用1000的地址,则涉及旧对象的回收,那原有对于1000的引用关系都会受到影响,所以只能开辟新的内存空间给1001
Python若支持i++,其操作过程要比C的i++复杂,而且其含义也不再是“令数字增加1”(自增),而是“创建一个新的数字”(新增),这样的话,“自增操作符”(incrementoperator)就名不副实了。
Python在理论上可以实现i++操作,但它就必须重新定义“自增操作符”,还会令有其它语言经验的人产生误解,不如就让大家直接写成i+=1或者i=i+1好了。
2、Python有可迭代对象
C/C++等语言设计出i++,最主要的目的是为了方便使用三段式的for结构:
for(inti=0;i<100;i++){
//执行xxx
}
这种程序关心的是数字本身的自增过程,数字做加法与程序体的执行相关联。
Python中没有这种for结构的写法,它提供了更为优雅的方式:
foriinrange(100):
#执行xxx
my_list=["你好","我是简乌兹","欢迎关注"]
forinfoinmy_list:
print(info)
这里体现了不同的思维方式,它关心的是在一个数值范围内的迭代遍历,并不关心也不需要人为对数字做加法。
Python中的可迭代对象/迭代器/生成器提供了非常良好的迭代/遍历用法,能够做到对i++的完全替代。
例如,上例中实现了对列表内值的遍历,Python还可以用enumerate实现对下标与具体值的同时遍历:
my_list=["你好","我是简乌兹","欢迎关注"]
fori,infoinenumerate(my_list):
print(i,info)
#打印结果:
0你好
1我是简乌兹
2欢迎关注
再例如对于字典的遍历,Python提供了keys、values、items等遍历方法,非常好用:
my_dict={'a':'1','b':'2','c':'3'}
forkeyinmy_dict.keys:
print(key)
forkey,valueinmy_dict.items:
print(key,value)
有了这样的利器,哪里还有i++的用武之地呢?
不仅如此,Python中基本上很少使用i+=1或者i=i+1,由于存在着随处可见的可迭代对象,开发者们很容易实现对一个数值区间的操作,也就很少有对于某个数值作累加的诉求了。
所以,回到我们开头的问题,其实这两种“自增”写法并没有胜出i++多少,只因为它们是通用型操作,又不需要引入新的操作符,所以Python才延续了一种基础性的支持。真正的赢家其实是各种各样的可迭代对象!
稍微小结下:Python不支持自增操作符,一方面是因为它的整数是不可变类型的一等公民,自增操作(++)若要支持,则会带来歧义;另一方面主要因为它有更合适的实现,即可迭代对象,对遍历操作有很好的支持。