当前位置: 首页 > news >正文

Python2.x和3.x主要差异总结

开始使用Python之后就到处宣扬Python如何如何好,宣传工作的一大重要诀窍就是做对比,比如原先用Java的时候做个什么东西要写多少代码,怎么个别扭,现在用Python实现同样的功能怎么个简单等等。不过谈Python,不管怎么谈,老会谈到Python2.x和3.x的版本差异问题,这个差异真不是一般的大,从一个简单的print到核心库的改进都牵扯到了很多,现在总结了一些主要的差异点。

基本类型

(1) 整形

在python 2.x中,有两种整数类型,一般的32位整数和长整数,长整数都是以L或者l(不建议使用小写l, 容易跟1搞混),超过32位长度之后会自动转换为长整形。

在python 3.x中,允许我们更随心所欲更自然的使用整数,只有一种类型,没有长度限制。

python 2.x

1>>> 1000000000000000000000000000000000
21000000000000000000000000000000000L

python 3.x

1>>> 1000000000000000000000000000000
21000000000000000000000000000000

(2) 八进制字面量表示

在Python 2.x中,表示八进制字面量有两种方式,一是同众多我们所熟悉的编程语言一样,直接在数字前加0,比如01000, 另外是加0o(0和小写字母o)0o1000

在Python 3.x中,表示八进制字面量的方式只有一种,就是0o1000

python 2.x

1>>> 0o1000
2512
3>>> 01000
4512

python 3.x

1>>> 01000
2  File "<stdin>", line 1
3    01000
4        ^
5SyntaxError: invalid token
6>>> 0o1000
7512

运算符

(1) 不等于测试

Python 2.x中不等于有两种写法 != 和 <>

Python 3.x中去掉了<>, 只有!=一种写法,还好,我从来没有使用<>的习惯

(2) 去掉了repr表达式``

Python 2.x 中反引号``相当于repr函数的作用

Python 3.x 中去掉了``这种写法,只允许使用repr函数,这样做的目的是为了使代码看上去更清晰么?不过我感觉用repr的机会很少,一般只在debug的时候才用,多数时候还是用str函数来用字符串描述对象。

(3) 除法运算

Python中的除法较其它语言显得非常高端,有套很复杂的规则。Python中的除法有两个运算符,/和//

首先来说/除法:

在python 2.x中/除法就跟我们熟悉的大多数语言,比如Java啊C啊差不多,整数相除的结果是一个整数,把小数部分完全忽略掉,浮点数除法会保留小数点的部分得到一个浮点数的结果。

在python 3.x中/除法不再这么做了,对于整数之间的相除,结果也会是浮点数。

Python 2.x:

1>>> 1 / 2
20
3>>> 1.0 / 2.0
40.5

Python 3.x:

1>>> 1/2
20.5

而对于//除法,这种除法叫做floor除法,会对除法的结果自动进行一个floor操作,在python 2.x和python 3.x中是一致的。

python 2.x:

1>>> -1 // 2
2-1

python 3.x:

1>>> -1 // 2
2-1

注意的是并不是舍弃小数部分,而是执行floor操作,如果要截取小数部分,那么需要使用math模块的trunc函数

python 3.x:

1>>> import math
2>>> math.trunc(1 / 2)
30
4>>> math.trunc(-1 / 2)
50

(4) 比较运算符

Python 2.x中允许不同类型的对象进行比较,比如:

1>>> -1 < ''
2True
3>>> 1 > ''
4False

Python 3.x中则不允许这类不同类型之间含糊不清的比较:

1>>> 1 > ''
2Traceback (most recent call last):
3  File "<stdin>", line 1in <module>
4TypeError: unorderable types: int() > str()

我觉着即使在2.x中也不应该使用这种含糊不清的比较,1 > '' 返回了False, 这个是基于什么判断的?说不清楚。

语句

(1) print

1-1、这是应该算是最广为人知的一个差别了吧,Python 2.x和Python 3.x之间连Hello World都是不兼容的。

python 2.x中print是语句

1print >> file x, y

向打开的输出流file中输出x, y变量的值

在python 3.x中这句要这么写

1print(x,y,file=file)

file参数定义的默认值是sys.stdout

1-2、由于print输出后结束(即会换行),而在python2.X里,你想不让一个输出结束(即不换行)要用:,;在python3.X里,就用: end=''

如:

在python3.X里用:

print('123',end='');

print('ABC')

而在python2.X里用:

print '123',

print('ABC')   #在输出字符串时可以不用()括号

(2) 扩展序列解包

python中的序列赋值一直是这门语言宣传时候的一个亮点,能把一个序列解开进行赋值:

01>>> x, y = [12]
02>>> x
031
04>>> y
052
06>>> x, y = 12
07>>> x
081
09>>> y
102
11>>> x, y = y, x
12>>> x
132
14>>> y
151

python 3.x对这一功能更加进行了强化,支持扩展序列解包:

1>>> x, *= 123
2>>> x
31
4>>> y
5[23]

内置集合类型

内置集合的差别主要体现在字典对象的几个视图方法上,keys\items和values,在2.x中这几个试图方法每次都是赤裸裸的返回一个新的列表,3.x对这种粗鲁的行为做了优化,返回的是迭代器对象。

另外原先字典对象有个has_key方法来判断key在字典中是否存在,这个方法实现的功能跟in运算符完全一样,因此在3.x就把这个方法给干掉了。


函数

(1) nonlocal作用域

在2.x的时代,Python只有两个作用域,模块里面的全局作用域和函数的局部作用域,但是随着在函数中定义函数的情况越来越多,比如装饰器、闭包等等,这里面就出现了内层函数引用外层函数变量的问题:

比如我要在内层函数修改外层函数的一个变量,在Python 2.x的时代就会出现错误:

01>>> def out_function():
02...   call_count = 0
03...   def in_function():
04...     call_count += 1
05...   return in_function
06...
07>>> out_function()()
08Traceback (most recent call last):
09  File "<stdin>", line 1in <module>
10  File "<stdin>", line 4in in_function
11UnboundLocalError: local variable 'call_count' referenced before assignment

但是在Python 3.x中只要使用nonlocal关键字对变量进行修饰,就会自动去外层函数寻找变量:

1>>> def out_function():
2...   call_count = 0
3...   def in_function():
4...     nonlocal call_count
5...     call_count+=1
6...   return in_function
7...
8>>> out_function()()

(2) Key-word only 参数

前面我们说到print在Python 3.x中是作为函数提供的。print的参数设计是这样的:

1print(*value, sep=' ', end='\n'file=sys.stdout)

如果了解Python参数的顺序规则,我们知道在Python 2.x中,参数的顺序必须遵循以下规则去定义:

def function(一般参数 or 带默认值参数,*sequence, **dict)

而这个地方却允许先定义*sequence再去定义一般参数,这就是Python 3.x所支持的key-word only的参数形式。在一个*之后允许去定义一些参数,这些参数在函数调用的时候必须指定参数名称。这样本质上其实就是在*sequence类型的参数之后固定写死了一个**dict, 当然也可以在后面继续定义一个**dict:

1def test(*value, name, **dict):

但这样写就不对了def test(*value, **dict, name)

(3) map、filter和reduce

这三个函数号称是函数式编程的代表。在Python3.x和Python2.x中也有了很大的差异。

首先我们先简单的在Python2.x的交互下输入map和filter,看到它们两者的类型是built-in function:

1>>> map
2<built-in function map>
3>>> filter
4<built-in function filter>

它们输出的结果类型都是列表:

1>>> map(lambda x:x * 2,[1,2,3])
2[246]
3>>> filter(lambda x:x % 2 == 0range(10))
4[02468]

但是在Python 3.x中它们却不是这个样子了:

1>>> map
2<class 'map'>
3>>> map(print,[1,2,3])
4<map object at 0xa6fd70c>
5>>> filter
6<class 'filter'>
7>>> filter(lambda x:x % 2 == 0range(10))
8<filter object at 0xa6eeeac>

首先它们从函数变成了类,其次,它们的返回结果也从当初的列表成了一个可迭代的对象, 我们尝试用next函数来进行手工迭代:

1>>> f = filter(lambda x:x % 2 == 0range(10))
2>>> next(f)
30
4>>> next(f)
52
6>>> next(f)
74
8>>> next(f)
96

对于比较高端的reduce函数,它在Python 3.x中已经不属于built-in了,被挪到functools模块当中。

模块

Python 2.x和3.x模块之间的差别主要体现在相对导入这部分的规则上。

在Python2.x和3.x当中都是使用点号来指定在当前包下进行相对导入,但是在没有点号的情况下,Python2.x会以先相对再绝对的模块搜索顺序,3.x的顺序跟这个相反,默认是绝对的,缺少点号的时候,导入忽略包含包自身并在sys.path搜索路径上进行查找。

面向对象

(1) 经典类和新式类

Python OO最神奇的地方就是有两种类,经典类和新式类。

新式类跟经典类的差别主要是以下几点:

1. 新式类对象可以直接通过__class__属性获取自身类型:type

2. 继承搜索的顺序发生了改变,经典类多继承属性搜索顺序: 先深入继承树左侧,再返回,开始找右侧;新式类多继承属性搜索顺序: 先水平搜索,然后再向上移动

3. 新式类增加了__slots__内置属性, 可以把实例属性的种类锁定到__slots__规定的范围之中。

4. 新式类增加了__getattribute__方法

Python 2.x中默认都是经典类,只有显式继承了object才是新式类

Python 3.x中默认都是新式类,不必显式的继承object

python 2.x:

1>>> ClassicClass.__class__
2Traceback (most recent call last):
3  File "<stdin>", line 1in <module>
4AttributeError: class ClassicClass has no attribute '__class__'
5>>> class NewClass(object):
6...   pass
7...
8>>> NewClass.__class__
9<type 'type'>

python 3.x:

1>>> class NewClass:pass
2...
3>>> NewClass.__class__
4<class 'type'>

(2) 无绑定方法

在Python 2.x中除了类方法和静态方法,其余的方法都必须在第一个参数传递self跟实例绑定,但是在Python 3.x中废除了这条规定,允许方法不绑定实例,这样的方法跟普通的函数没有区别:

Python 2.x:

01>>> class MyClass:
02...   def function():
03...     print "function"
04...
05>>> MyClass.function()
06Traceback (most recent call last):
07  File "<stdin>", line 1in <module>
08TypeError: unbound method function() must be called with MyClass instance as first argument (got nothing instead)
09>>> m = MyClass()
10>>> m.function()
11Traceback (most recent call last):
12  File "<stdin>", line 1in <module>
13TypeError: function() takes no arguments (1 given)

Python 3.x:

01>>> class MyClass:
02...   def function():
03...     print("function")
04...
05>>> MyClass.function()
06function
07>>> m = MyClass()
08>>> m.function()
09Traceback (most recent call last):
10  File "<stdin>", line 1in <module>
11TypeError: function() takes no arguments (1 given)

(3) 重要的重载

1. next()和__next__():这应该是继print之后第二大坑爹的不兼容吧,Python程序漫山遍野都是迭代器,但是2和3之间迭代器的实现接口方法名是不同的……嗯,啥都不说了。

2. 分片拦截:Python 3.x之前, 类可以定义__getslice__和__setslice__方法来专门拦截分片,并且这两个方法优先于__getitem__和__setitem__, 但是Python 3.x时代这俩方法再也不存在了,全部的工作都交给了__getitem__和__setitem__,因此在使用这两个方法之前要先判断传递进参数的类型是不是slice对象。

3. __bool__方法:我们知道Python中默认将所有的空对象定义为布尔意义上的False,在自己定义的类中我们也可以加入自定义的布尔判断标准,在2.x中这个方法名叫做__nonzero__, 这个名字显然非常不直观并且不科学!所有考试交白卷的孩子我们都要否定他们的才能么?显然不能!因此Python 3.x中这个方法被重名命为__bool__

4. 3.x 取消了用于大小比较的__cmp__方法,取而代之的是:__lt__、__gt__、__le__、__ge__、__eq__、__ne__,嗯,我感觉这个想法真是不能苟同……有谁能说服我给我洗脑让我爱上这一堆__lt__、__gt__、__le__、__ge__、__eq__、__ne__么。。。

(4) 类修饰器

在我的上一篇博客中秀了一把函数装饰器在表单验证中的使用,http://my.oschina.net/chihz/blog/122897

在3.x的时代,类也有装饰器了,这个装饰器威力巨大,能把装饰的类搞的面目全非,总之想怎么搞就怎么搞,用法同函数装饰器基本一致,只不过传递的参数是类型:

01>>> def shutdown(cls):
02...   def shutdown_func(self):
03...     print("do something...")
04...   cls.shutdown = shutdown_func
05...   return cls
06...
07>>> @shutdown
08... class Test:pass
09...
10>>> t = Test()
11>>> t.shutdown()
12do something...

异常

先来看一段代码

python 2.x:

01>>> class Person:
02...   def __init__(self, msg):
03...     self.msg = msg
04...
05>>> try:
06...   raise Person, "woca"
07... except Person as p:
08...   print p.msg
09...
10woca

python 3.x:

01>>> class Person:
02...   def __init__(self, msg):
03...     self.msg = msg
04...
05>>> try:
06...   raise Person("woca")
07... except Person as p:
08...   print(p.msg)
09Traceback (most recent call last):
10  File "<stdin>", line 2in <module>
11TypeError: exceptions must derive from BaseException
12
13During handling of the above exception, another exception occurred:
14
15Traceback (most recent call last):
16  File "<stdin>", line 3in <module>
17TypeError: catching classes that do not inherit from BaseException is not allowed
18>>>

接下来说不同:

1. 在2.x时代,所有类型的对象都是可以被直接抛出的,在3.x时代,只有继承自BaseException的对象才可以被抛出。

2. 2.x raise语句使用逗号将抛出对象类型和参数分开,3.x取消了这种奇葩的写法,直接调用构造函数抛出对象即可。

在2.x时代,异常在代码中除了表示程序错误,还经常做一些普通控制结构应该做的事情,在3.x中可以看出,设计者让异常变的更加专一,只有在错误发生的情况才能去用异常捕获语句来处理。

其它

(1) 编码

Py3.X源码文件默认使用utf-8编码, 不需要再在文件头上声明 # -*- coding: utf-8 -*- 

(2) input函数

取消了蛋疼的raw_input,input函数的功能跟原先的raw_input一样

python 2.x

1>>> x = input("please input x:")
2please input x:1
3>>> x
41

python 3.x

1>>> x = input("please input x")
2please input x1
3>>> x
4'1'

(2) 返回结果 列表->迭代器

取消了原先的xrange,现在range函数的返回结果同之前的xrange的返回结果一样,不是列表,而是一个可迭代的对象。其它常用的内置函数,如zip也是如此。

(3) Unicode字符串

能很好的支持Unicode

python 2.x

1>>> str = "我爱北京天安门"
2>>> str
3'\xe6\x88\x91\xe7\x88\xb1\xe5\x8c\x97\xe4\xba\xac\xe5\xa4\xa9\xe5\xae\x89\xe9\x97\xa8'
4>>> str = u"我爱北京天安门"
5>>> str
6u'\u6211\u7231\u5317\u4eac\u5929\u5b89\u95e8'

python 3.x

1>>> str = "我爱北京天安门"
2>>> str
3'我爱北京天安门'

没有去考究核心库的差别,常用语法上的差别就总结了这些,感觉……还好吧,很多常用的框架现在都开始支持Python 3.x, 虽然现在自己主要是在用2.7进行开发,但多了解一下最新的动态总是好的,Python 最初版本设计的年代距离现在已经很长时间了,貌似跟自己同岁,可能那时的一些概念尚不清晰,但Python出现的比Java早多了,能设计到这种先进地步,已经相当厉害,Python 3.x虽然跟Python 2.x不兼容,但是修改之处都是让Python看起来更合理,所有的风格更一致,更Pythonic,总之是个好方向。

相关文章:

  • Vue中引入react组件
  • python的8大核心语句,你确定不来看看嘛,那格局就小啦
  • windows排查问题常用命令
  • 2023年网络安全比赛--跨站脚本攻击中职组(超详细)
  • SkyEye:针对飞行模拟器的仿真解决方案
  • 基于Python + Django 的密码自助平台项目(完整代码)
  • 写作的“收益”超乎想象
  • 前端号外—2022年明星项目居然是它,Node.js危已?
  • 六道算法基础题详解
  • 【Linux】在Linux上写一个进度条小程序
  • C生万物 | 函数的讲解与剖析【内附众多案例详解】
  • Spring为什么这么火 之 Spring蕴含的设计思想
  • 来自一位双非本科大二学生的?自我救赎:堕落——蜕变
  • 看完RabbitMQ了的几种消息模型,我对RabbitMQ有了新的认识
  • Java字节流基础详解(InputStream/OutputStream)
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • es6--symbol
  • Linux快速复制或删除大量小文件
  • mysql innodb 索引使用指南
  • python docx文档转html页面
  • Sass 快速入门教程
  • Spring Security中异常上抛机制及对于转型处理的一些感悟
  • springboot_database项目介绍
  • SpriteKit 技巧之添加背景图片
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • Travix是如何部署应用程序到Kubernetes上的
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 关于extract.autodesk.io的一些说明
  • 前端面试之闭包
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • 移动端 h5开发相关内容总结(三)
  • MyCAT水平分库
  • Semaphore
  • 东超科技获得千万级Pre-A轮融资,投资方为中科创星 ...
  • # 达梦数据库知识点
  • #stm32驱动外设模块总结w5500模块
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (PyTorch)TCN和RNN/LSTM/GRU结合实现时间序列预测
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (第9篇)大数据的的超级应用——数据挖掘-推荐系统
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (黑客游戏)HackTheGame1.21 过关攻略
  • (算法二)滑动窗口
  • (已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法
  • *上位机的定义
  • .NET “底层”异步编程模式——异步编程模型(Asynchronous Programming Model,APM)...
  • .net core 调用c dll_用C++生成一个简单的DLL文件VS2008
  • .NET/C# 中你可以在代码中写多个 Main 函数,然后按需要随时切换
  • .net6 webapi log4net完整配置使用流程
  • .NET基础篇——反射的奥妙
  • .py文件应该怎样打开?