Python2 到 Python3的迁移指南(差异)

   

目录

引言

1. print语句

2.Unicode字符串

3.unicode和str函数

4.long 长整型

5. <>比较运算符

6.字典中key值检测方法

7. dict对象中返回列表的方法

8.import导入路径的变化

9.filter()函数返回值的改变

10.map()函数返回值的改变

11.reduce()函数的变化

12.exec语句改为函数调用 

13.try...except语句的变化

14.raise语句的改变

15.xrange被移除,range返回迭代器

16.raw_input()和input()函数的变化

17.函数元属性的命名改变

18.文件对象的xreadlines()函数被移除

19.lambda的变化

20.basestring被移除

21.itertools的变化

22.tuple在列表解析中的变化

23.``(反引号)


引言

随着时间的推进,python3已经逐渐成为主流,python2也逐渐成为历史。但是这不意味的万事大吉,对于那些在python3成为主流以前的基于python2的项目来说,面临2个选择,要么用python3重构,要么从python2迁移到python3,对代码进行升级。重构或迁移主要是技术决策者要考虑的事情,对我们干活的人来说,就要随时做好准备,不管是重构还是迁移,了解2个版本之前的差异都是必要的。基于这个原因,本文对python2和python3的区别做了综合性的总结,希望对于熟悉python2,但是对python3还不熟悉却又想考虑使用的人是一个有益的指导。

1. print语句

在python2中,print是一个语句:

print
print 'hello,World!'
print 1
print 1,
print >>sys.stderr,1,2,3

在python3中,print成为一个函数,用法也和调用函数无异:

print()
print('hello,World!')
print(1)
print(1, 2)
print(1, 2, end=' ')
print(1, 2, 3, file=sys.stderr)

2.Unicode字符串

在python2中,字符串分为unicode和非unicode 2类。而在python3中,所有字符串都是unicode的。

python2中定义字符串需要加前导字符u:

u'Hello, World'
ur'Hello,\World'

在python3中就不需要了:

'Hello, World'
r'Hello,\World'

3.unicode和str函数

在python2中,一个对象要转为unicode字符串,可以使用unicode()函数,把对象转为非unicode字符串可以用str()函数。在python3中,基于第2条说明,unicode函数已经不需要也不存在了。str函数保留原有功能,同时将返回结果处理为unicode字符串。

str(object)

4.long 长整型

在python2里有int和long都表示整型数,int的最大值定义在sys.maxint中(我的电脑上是 9223372036854775807),超过这个数的就需要用long类型来表示了。如果想显示的定义一个long型,需要在数字后面加L或小写l。

>>> a=sys.maxint
>>> type(a)
<type 'int'>
>>> b=a+1
>>> type(b)
<type 'long'>
>>> c=1L
>>> type(c)
<type 'long'>

在python3中,只有一种整型 int,可以表示任意一个整数,因此long也就不需要了:

>>> sys.maxsize
9223372036854775807
>>> long(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'long' is not defined
>>> a=sys.maxsize
>>> type(a)
<class 'int'>
>>> b=a+1
>>> type(b)
<class 'int'>
>>> b
9223372036854775808
>>> b**30
88448771443952937988070644155037561422858825544533428496495779911601139499522777157934184566439227216106791839364897366036734335634912102738537861483096452501590794500366037505070655509492154554330380012187428569493313327601290644792165407831275752714974250153462485472760588879376768193944545983701113594651492775803125944164632554194156221540393238651200515281509096320752698232834711818187909914434986938220058194138506237353577333336102788317629403447047617577968414072793190825064536181674655498943658601158166803883275870027173333526136193657844232347425622196224
>>> c=b**30
>>> type(c)
<class 'int'>

你可以把b**30改为b**3000看看,不过要小心,如果你的电脑性能不够好,可能要等一会儿才能出来结果。

5. <>比较运算符

在python2中,<> 和 != 都表示是进行不相等比较的运算符,例如:

>>> 1 <> 2
True
>>> 1 != 2
True
>>> 1 <> 2 <> 3
True
>>> 1 <> 2 != 3
True

在python3中,不再支持 <>了,只有 !=运算符了:

>>> 1<>2
  File "<stdin>", line 1
    1<>2
      ^
SyntaxError: invalid syntax
>>> 1!=2
True
>>> 1!=2!=3
True

6.字典中key值检测方法

在python2中,检测某个key是否在一个字典变量中存在,可以用 has_key()函数,或者是 in 判断:

>>> d={'name':'KM'}
>>> 'name' in d
True
>>> d.has_key('name')
True
>>> d.has_key('age')
False
>>> 'age' in d
False

在python3中,has_key()函数不存在了,可以统一使用 in 来进行检测:

>>> d={'name':'KM'}
>>> 'name' in d
True
>>> d.has_key('name')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'has_key'

7. dict对象中返回列表的方法

在python2中,要获取一个dict对象的所有keys,可以使用keys()方法。

>>> d={'name':'KM'}
>>> d.keys()
['name']
>>> d.keys()[0]
'name'

返回值是一个list,可以使用索引获取某一项。在python3中,keys()返回值的结果不再是你预期的list,因此也不能再简单的使用索引来获取里面的值:

>>> d
{'name': 'KM'}
>>> d.keys()
dict_keys(['name'])
>>> d.keys()[0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'dict_keys' object is not subscriptable

要解决这个问题,可以简单的把keys()的返回值值给list即可:

>>> list(d.keys())[0]
'name'

对于items()和values()函数,也需要同样处理。对于一个数据量比较大的dict对象,考虑到性能问题,可以进一步通过获取一个迭代器来遍历数据,只需要把list换为iter:

>>> iter(d.keys())
<dict_keyiterator object at 0x10d98b590>

如果想进一步了解keys(),values(),items()的返回值类型的详细信息,请参考字典视图对象

8.import导入路径的变化

在python2中,在import模块时,会先在当前目录中搜索,然后在sys.path的路径中搜索。而在python3中,会直接去sys.path的路径中搜索。这样对于一个包内不同的模块来说,互相引用时需要用一种相对路径的方式来引用。比如对于某一个包:

chardet
    |--__init__.py
    |--constants.py
    |--mbcharsetprober.py
    |--universaldetector.py

如果在universaldetector.py中要导入constants.py模块和mbcharsetprober.py的一个类,在python2中

import constants	
from mbcharsetprober import MultiByteCharSetProber	

而在python3中是这样的:

from . import constants	
from .mbcharsetprober import MultiByteCharSetProber	

9.filter()函数返回值的改变

在python2中,全局函数filter的返回值是一个列表:

>>> r
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> def odd(v):
...   if v % 2==1:
...     return True
...   return False
...
>>> filter(odd,r)
[1, 3, 5, 7, 9]

在python3中,filter()函数的返回值不再是列表,而是一个迭代器:

>>> r=range(10)
>>> r
range(0, 10)
>>> def odd(v):
...   if v % 2 ==1:
...     return True
...   return False
...
>>> filter(odd,r)
<filter object at 0x10d9941d0>
>>> list(filter(odd,r))
[1, 3, 5, 7, 9]

filter的第二个参数即可以是一个list 或 tuple的序列,也可以是一个可以迭代的对象,像上面的例子一样。需要注意的是,range()函数的返回值在python2中是一个list,而在python3中就是一个可迭代的对象。

10.map()函数返回值的改变

与filter()函数类似,map()函数的返回值在python3中也是一个迭代器,在使用时都需要按filter()函数的处理方式做相应调整。

>>> map(odd,r)
<map object at 0x10d9ad510>
>>> list(map(odd,r))
[False, True, False, True, False, True, False, True, False, True]

11.reduce()函数的变化

在python2中,可以直接使用reduce函数来对一个序列依次取2 个值进行处理,在python3中,reduce函数被挪到了functools包中,使用时需要先导入:

from functools import reduce

额外说一句,想想为什么要进行这个改动或许会帮助你找到更有效的数据处理方式。

12.exec语句改为函数调用 

和print类似,exec的使用方式从语句形式,变为函数调用。在python2中这样使用:

>>> exec 'print "hello,world!"'
hello,world!

在python3中则是:

>>> exec('print("hello,world!")')
hello,world!

13.try...except语句的变化

在捕获和处理异常时,对异常对象名称的指定需要通过as声明。在python2中,try可能是这样:

>>> try:
...  a=1/0
... except Exception, e:
...  print e.args
...
('integer division or modulo by zero',)

在python3中这么写会出现语法错误,需要改为:

>>> try:
...  a=1/0
... except Exception,e:
  File "<stdin>", line 3
    except Exception,e:
                    ^
SyntaxError: invalid syntax
>>>
>>> try:
...  a=1/0
... except Exception as e:
...  print(e)
...
division by zero
>>>

14.raise语句的改变

抛出异常的用法在python2和python3之间有细微变化,主要是异常参数传递的差别上。在python2上是:

>>> raise Exception, "My error message"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Exception: My error message
>>>

而在python3上则是:

>>> raise Exception('My error message')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Exception: My error message
>>>

15.xrange被移除,range返回迭代器

在python2中,range返回一个列表,xrange返回一个迭代器。在python3中,xrange已经移除,只有range,并且range返回的不再是列表,而是一个迭代器。在python2中:

>>> r=range(10)
>>> r
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> xr=xrange(10)
>>> xr
xrange(10)
>>> list(xr)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>

python3的环境:

>>> r=range(10)
>>> r
range(0, 10)
>>> list(r)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> xr=xrange(10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'xrange' is not defined

在数据量小的时候,可以简单的外加list来达到python2 range的效果。但是在大数据量时,迭代器就能体现出对内在性能优化的优势。

16.raw_input()和input()函数的变化

在python2中,有2个全局函数用来获取用户从命令行输入的数据,一个是raw_input,它以字符串形式返回用户输入的内容;另一个是input,它把输入当作一个计算表达式,返回表达式的计算结果。

>>> ie=input()
100*3==True
>>> ie
False
>>> ie=input()
100*3==300
>>> ie
True
>>> ir=raw_input()
True
>>> ir
'True'
>>> raw_input()
100*3==300
'100*3==300'
>>>

在python3中,input函数的功能等同于python2中的raw_input,若想像python2中的input一样求表达式的结果,可以对返回值加eval进行计算。

>>> ie=input()
100*3==300
>>> ie
'100*3==300'
>>> eval(ie)
True
>>> cm=input()
print('hello')
>>> cm
"print('hello')"
>>> eval(cm)
hello
>>>

特别注意:eval会执行任何合法的python语句,如果不对输入进行检查,可能会导入严重漏洞。

17.函数元属性的命名改变

在python2中,获取函数的元属性可以通过func_*开头的属性,例如:

>>> def fun(a=0):
...  '''function fun'''
...  print(a)
...
>>> dir(fun)
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
>>> fun.func_doc
'function fun'
>>> fun.func_name
'fun'

在python3中,元属性中func_开头的属性都已经被移除,可以通过对应的双下划线开头的属性来获取:

>>> def fun(a=0):
...  ''' function fun'''
...  print(a)
...
>>> dir(fun)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> fun.__doc__
' function fun'
>>> fun.__name__
'fun'
>>> fun.__class__
<class 'function'>
>>>

18.文件对象的xreadlines()函数被移除

在python2中,当读取一个大文件时,考虑到内存性能问题,会选择使用文件对象的xreadlines函数,它返回一个迭代器,避免一次把文件内容全部读取到内存中。

>>> myfile=open('t.txt')
>>> mr=myfile.xreadlines()
>>> mr
<open file 't.txt', mode 'r' at 0x10fbec660>
>>> line=mr.next()
>>> line
'0\n'
>>> print(line)
0

>>> line=mr.next()
>>> line
'1\n'
>>> print(line)
1

在python3中,文件对象本身就是一个迭代器,所以不需要xreadlines函数了,已经被移除。

>>> myfile=open('t.txt')
>>> myfile
<_io.TextIOWrapper name='t.txt' mode='r' encoding='UTF-8'>
>>> dir(myfile)
['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'reconfigure', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'write_through', 'writelines']
>>> line=next(myfile)
>>> line
'0\n'
>>> print(line)
0

19.lambda的变化

在python2中,可以这样定义和使用:

>>> l1=lambda x,y:x*y
>>> l1(5,2)
10
>>> l1((2,5))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: <lambda>() takes exactly 2 arguments (1 given)
>>> l2=lambda (x,y):x*y
>>> l2((2,5))
10
>>> l2(2,5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: <lambda>() takes exactly 1 argument (2 given)

在python3中,定义时不能在指定参数时加小括号。对于要以tuple形式传递参数的情形,可以在tuple前面加*来实现。

>>> l=lambda (x,y):x*y
  File "<stdin>", line 1
    l=lambda (x,y):x*y
             ^
SyntaxError: invalid syntax
>>> l=lambda x,y:x*y
>>> l(1,2)
2
>>> l(3,2)
6
>>> l((2,5))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'y'
>>> l(*(2,5))
10

20.basestring被移除

在python2中,有unicode字符串和非unicode字符串,它们其实都是basestring类型的:

>>> s='hello'
>>> isinstance(s,basestring)
True
>>> u=u'中华人民共和国'
>>> isinstance(s,basestring)
True
>>> isinstance(s,str)
True
>>> isinstance(s,unicode)
False
>>> isinstance(u,unicode)
True
>>> isinstance(u,str)
False
>>>

在python3中,只有unicode一种类型的字符串,所以不需要basestring了,已经被移除。

>>> s='hello'
>>> u='中华人民共和国'
>>> isinstance(s,str)
True
>>> isinstance(u,str)
True
>>> isinstance(u,basestring)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'basestring' is not defined
>>>

21.itertools的变化

在pytohon2中,itertools模块中有izip,imap和ifilter,他们用于获取一个给定序列应用某个函数之后的迭代器,例如:

>>> import itertools as it
>>> r=range(10)
>>> def odd(v):
...  return v % 2 ==1
...
>>> odds=it.ifilter(odd,r)
>>> odds
<itertools.ifilter object at 0x10fc94310>
>>> list(odds)
[1, 3, 5, 7, 9]
>>>

在python3中,zip,map和filter返回的结果本身就是迭代器了,所以在itertools中已经不需要额外再定义类似功能的函数了。

>>> import itertools as it
>>> dir(it)
['__doc__', '__loader__', '__name__', '__package__', '__spec__', '_grouper', '_tee', '_tee_dataobject', 'accumulate', 'chain', 'combinations', 'combinations_with_replacement', 'compress', 'count', 'cycle', 'dropwhile', 'filterfalse', 'groupby', 'islice', 'permutations', 'product', 'repeat', 'starmap', 'takewhile', 'tee', 'zip_longest']
>>> r=range(10)
>>> def odd(v):
...  return v % 2 ==1
...
>>> odds=filter(odd,r)
>>> odds
<filter object at 0x10dd76390>
>>> list(odds)
[1, 3, 5, 7, 9]
>>>

22.tuple在列表解析中的变化

在python2中对一个未明确声明的tuple可以被正确处理,例如:

>>> [i**2 for i in 1,2,3]
[1, 4, 9]
>>>

在python3中,这会导致语法错误,外加括号即可解决:

>>> [i**2 for i in 1,2,3]
  File "<stdin>", line 1
    [i**2 for i in 1,2,3]
                    ^
SyntaxError: invalid syntax
>>> [i**2 for i in (1,2,3)]
[1, 4, 9]
>>>

23.``(反引号)

在python2中,可以对一个对象用``取它的字符串表示:

>>> s
'hello'
>>> `s`
"'hello'">>> exec('print("hello,world!")')
hello,world!

在python3中,``不可用,相同功能需要repr函数调用来实现:

>>> `s`
  File "<stdin>", line 1
    `s`
    ^
SyntaxError: invalid syntax
>>> repr(s)
"'hello'"

以上就是总结的我觉得相对需要注意的差异,当然还有其它一些不同,或者不那么重要的,没有全都列出。在迁移时如果遇到其它问题,可以通过查阅官方文档来找到解决方法。

Python是一个不断完善发展的语言,目前我们已经能看到它从最初的状态到现在它在很多领域的应用,相信以后还会有变化,但是无论如何,python在不断完善,适应时代,我们也需要不断完善,提升自己。

也欢迎大家提出你认为重要的变化,让这个列表可以更完整。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值