Python 为什么不支持 i++ 自增语法,不提供 ++ 操作符?
在 C/C++/Java 等等语言中,整型变量的自增或自减操作是标配,它们又可分为前缀操作(++i 和 --i)与后缀操作(i++ 和 i--),彼此存在着一些细微差别,各有不同的用途。
这些语言的使用者在接触 Python 时,可能会疑惑为什么它不提供 ++ 或 -- 的操作呢?在我前不久发的《Python的十万个为什么?》里,就有不少同学在调查问卷中表示了对此话题感兴趣。
Python 中虽然可能出现 ++i 这种前缀形式的写法,但是它并没有“++”自增操作符,此处只是两个“+”(正数符号)的叠加而已,至于后缀形式的“++”,则完全不支持(SyntaxError: invalid syntax)。
本期“Python为什么 ”栏目,我们将会从两个主要的角度来回答:Python 为什么不支持 i++ 自增语法? (PS:此处自增指代“自增和自减”,下同)
首先,Python 当然可以实现自增效果,即写成i += 1
或者 i = i + 1
,这在其它语言中也是通用的。
虽然 Python 在底层用了不同的魔术方法(__add__()
和 __iadd__()
)来完成计算,但表面上的效果完全相同。
所以,我们的问题可以转化成:为什么上面的两种写法会胜过 i++,成为 Python 的最终选择呢?
1、Python 的整数是不可变类型
当我们定义i = 1000
时,不同语言会作出不同的处理:
- C 之类的语言(写法
int i = 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”(自增),而是“创建一个新的数字”(新增), 这样的话,“自增操作符”(increment operator)就名不副实了。
Python 在理论上可以实现 i++ 操作,但它就必须重新定义“自增操作符”,还会令有其它语言经验的人产生误解,不如就让大家直接写成i += 1
或者 i = i + 1
好了。
2、Python 有可迭代对象
C/C++ 等语言设计出 i++,最主要的目的是为了方便使用三段式的 for 结构:
for(int i = 0; i < 100; i++){
// 执行 xxx
}
这种程序关心的是数字本身的自增过程,数字做加法与程序体的执行相关联。
Python 中没有这种 for 结构的写法,它提供了更为优雅的方式:
for i in range(100):
# 执行 xxx
my_list = ["你好", "我是Python猫", "欢迎关注"]
for info in my_list:
print(info)
这里体现了不同的思维方式,它关心的是在一个数值范围内的迭代遍历,并不关心也不需要人为对数字做加法。
Python 中的可迭代对象/迭代器/生成器提供了非常良好的迭代/遍历用法,能够做到对 i++ 的完全替代。
例如,上例中实现了对列表内值的遍历,Python 还可以用 enumerate() 实现对下标与具体值的同时遍历:
my_list = ["你好", "我是Python猫", "欢迎关注"]
for i, info in enumerate(my_list):
print(i, info)
# 打印结果:
0 你好
1 我是Python猫
2 欢迎关注
再例如对于字典的遍历,Python 提供了 keys()、values()、items() 等遍历方法,非常好用:
my_dict = {'a': '1', 'b': '2', 'c': '3'}
for key in my_dict.keys():
print(key)
for key, value in my_dict.items():
print(key, value)
有了这样的利器,哪里还有 i++ 的用武之地呢?
不仅如此,Python 中基本上很少使用i += 1
或者 i = i + 1
,由于存在着随处可见的可迭代对象,开发者们很容易实现对一个数值区间的操作,也就很少有对于某个数值作累加的诉求了。
所以,回到我们开头的问题,其实这两种“自增”写法并没有胜出 i++ 多少,只因为它们是通用型操作,又不需要引入新的操作符,所以 Python 才延续了一种基础性的支持。真正的赢家其实是各种各样的可迭代对象!
稍微小结下:Python 不支持自增操作符,一方面是因为它的整数是不可变类型的一等公民,自增操作(++)若要支持,则会带来歧义;另一方面主要因为它有更合适的实现,即可迭代对象,对遍历操作有很好的支持。
如果你觉得本文分析得不错,那你应该会喜欢这些文章:
4、Python 为什么没有 main 函数?为什么我不推荐写 main 函数?
写在最后:本文属于“Python为什么”系列(Python猫出品),该系列主要关注 Python 的语法、设计和发展等话题,以一个个“为什么”式的问题为切入点,试着展现 Python 的迷人魅力。部分话题会推出视频版,请在 B 站收看,观看地址:视频地址
公众号【Python猫】, 本号连载优质的系列文章,有Python为什么系列、喵星哲学猫系列、Python进阶系列、好书推荐系列、技术写作、优质英文推荐与翻译等等,欢迎关注哦。
Python 为什么不支持 i++ 自增语法,不提供 ++ 操作符?的更多相关文章
- Python 为什么能支持任意的真值判断?
本文出自"Python为什么"系列,请查看全部文章 Python 在涉及真值判断(Truth Value Testing)时,语法很简便. 比如,在判断某个对象是否不为 None ...
- 【转】让Souce Insight支持多种语言的语法高亮:Python,Ruby,ARM汇编,windows脚本文件(bat/batch),PPC,SQL,TCL,Delphi等
原文网址:http://www.crifan.com/source_insight_support_highlight_for_python_ruby_arm_batch_ppc_sql_tcl_de ...
- Python 为什么不支持 switch 语句?
本文出自"Python为什么"系列,请查看全部文章 在这篇文章里,我们会聊一聊为什么 Python 决定不支持 switch 语句. 为什么想要聊这个话题呢? 主要是因为 swit ...
- oracle+ibatis 批量插入-支持序列自增
首先请先看我前面一篇帖子了解oracle批量插入的sql:[oracle 批量插入-支持序列自增] 我用的ibatis2.0,sqlMap文件引入的标签如下: <!DOCTYPE sqlMap ...
- oracle 批量插入-支持序列自增
1.创建表.序列 -- Create table create table test_batch ( id number not null, name ), account ) ) -- Create ...
- 【原创】C++链表如何像Python List一样支持多种数据类型
用过Python的码友都知道,Python中List支持多种数据类型,如下面代码所示链表li内的数据类型可以是整数,同时也可以是字符串,当然也可以是其他数据类型. 1: >>> li ...
- python操作三大主流数据库(8)python操作mongodb数据库②python使用pymongo操作mongodb的增删改查
python操作mongodb数据库②python使用pymongo操作mongodb的增删改查 文档http://api.mongodb.com/python/current/api/index.h ...
- python操作三大主流数据库(2)python操作mysql②python对mysql进行简单的增删改查
python操作mysql②python对mysql进行简单的增删改查 1.设计mysql的数据库和表 id:新闻的唯一标示 title:新闻的标题 content:新闻的内容 created_at: ...
- 阿里云 rds python sdk不支持python3处理
阿里云文档中心的python版本aliyun-python-sdk-rds不支持python3处理 问题:默认情况下文档中心的python版本只支持python2,不兼容python3版本 需要稍微修 ...
随机推荐
- NO.3 MSP432P4_SDK浏览
网上关于MSP432的参考资料很少,我们要学习的最权威的资源只有TI提供的SDK.这是好处也是坏处,好处是我们学习的是TI一手资源,不再是拾人牙慧:坏处是英语能力要求较高. 闲话少说,我们先来看SDK ...
- 在win10上搭建pyspark,
最近在研究Spark,准确的说是pyspark,为了搭个测试环境,之前一直在云上开Linux机器来搭Hadoop,但是Spark可以Local执行,我就打算在本地搭个环境.遇到了一些问题,记录一下,也 ...
- [注]一将功成万骨枯!App的七种死法
一将功成万骨枯,这种事在有泡沫的行业总是会发生的.移动互联网尤甚.从<愤怒的小鸟>到<植物大战僵尸>.<捕鱼达人>.<唱吧>.<陌陌>……一 ...
- [安卓基础] 008.Android中的显示单位
*:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...
- Java IO(十六)InputStreamReader 和 InputStreamWriter
Java IO(十六)InputStreamReader 和 InputStreamWriter 一.介绍 InputStreamReader 和 OutputStreamWriter 是字节流通向字 ...
- PreparedStatement实现表数据的增删改 & 封装数据库链接和关闭操作
PreparedStatement实现表数据的增删改 PreparedStatementUpdateTest package com.aff.PreparedStatement; import jav ...
- 50个SQL语句(MySQL版) 问题十一
--------------------------表结构-------------------------- student(StuId,StuName,StuAge,StuSex) 学生表 tea ...
- hdl - HLS vs. Generator
https://mp.weixin.qq.com/s/n_4RKlOddr_p2S_wODvFbw 介绍硬件建模的各个层次,以及基于RTL进一步提高层次的方法. 1. 物理版图 直接画 ...
- 空格与false
- Java实现蓝桥杯第八届决赛 对局匹配
标题:对局匹配 小明喜欢在一个围棋网站上找别人在线对弈.这个网站上所有注册用户都有一个积分,代表他的围棋水平. 小明发现网站的自动对局系统在匹配对手时,只会将积分差恰好是K的两名用户匹配在一起.如果两 ...