因为Sublime 3 使用Python3,不得不将代码一直到python 3上面,差别还很多啊!html
几乎全部的Python 2程序都须要一些修改才能正常地运行在Python 3的环境下。为了简化这个转换过程,Python 3自带了一个叫作2to3
的实用脚本(Utility Script),这个脚本会将你的Python 2程序源文件做为输入,而后自动将其转换到Python 3的形式。案例研究:将chardet
移植到Python 3(porting chardet to Python 3)描述了如何运行这个脚本,而后展现了一些它不能自动修复的状况。这篇附录描述了它可以自动修复的内容。python
print
语句#在Python 2里,print
是一个语句。不管你想输出什么,只要将它们放在print
关键字后边就能够。在Python 3里,print()
是一个函数。就像其余的函数同样,print()
须要你将想要输出的东西做为参数传给它。程序员
Notes | Python 2 | Python 3 |
---|---|---|
① | print |
print() |
② | print 1 |
print(1) |
③ | print 1, 2 |
print(1, 2) |
④ | print 1, 2, |
print(1, 2, end=' ') |
⑤ | print >>sys.stderr, 1, 2, 3 |
print(1, 2, 3, file=sys.stderr) |
为输出一个空白行,须要调用不带参数的print()
。浏览器
为输出一个单独的值,须要将这这个值做为print()
的一个参数就能够了。安全
为输出使用一个空格分隔的两个值,用两个参数调用print()
便可。性能优化
这个例子有一些技巧。在Python 2里,若是你使用一个逗号(,)做为print
语句的结尾,它将会用空格分隔输出的结果,而后在输出一个尾随的空格(trailing space),而不输出回车(carriage return)。在Python 3里,经过把end=' '
做为一个关键字参数传给print()
能够实现一样的效果。参数end
的默认值为'\n'
,因此经过从新指定end
参数的值,能够取消在末尾输出回车符。服务器
在Python 2里,你能够经过使用>>pipe_name
语法,把输出重定向到一个管道,好比sys.stderr
。在Python 3里,你能够经过将管道做为关键字参数file
的值传递给print()
来完成一样的功能。参数file
的默认值为std.stdout
,因此从新指定它的值将会使print()
输出到一个另一个管道。cookie
Python 2有两种字符串类型:Unicode字符串和非Unicode字符串。Python 3只有一种类型:Unicode字符串(Unicode strings)。app
Notes | Python 2 | Python 3 |
---|---|---|
① | u'PapayaWhip' |
'PapayaWhip' |
② | ur'PapayaWhip\foo' |
r'PapayaWhip\foo' |
Python 2里的Unicode字符串在Python 3里即普通字符串,由于在Python 3里字符串老是Unicode形式的。socket
Unicode原始字符串(raw string)(使用这种字符串,Python不会自动转义反斜线"\")也被替换为普通的字符串,由于在Python 3里,全部原始字符串都是以Unicode编码的。
unicode()
#Python 2有两个全局函数能够把对象强制转换成字符串:unicode()
把对象转换成Unicode字符串,还有str()
把对象转换为非Unicode字符串。Python 3只有一种字符串类型,Unicode字符串,因此str()
函数便可完成全部的功能。(unicode()
函数在Python 3里再也不存在了。)
Notes | Python 2 | Python 3 |
---|---|---|
unicode(anything) |
str(anything) |
long
长整型#Python 2有为非浮点数准备的int
和long
类型。int
类型的最大值不能超过sys.maxint
,并且这个最大值是平台相关的。能够经过在数字的末尾附上一个L
来定义长整型,显然,它比int
类型表示的数字范围更大。在Python 3里,只有一种整数类型int
,大多数状况下,它很像Python 2里的长整型。因为已经不存在两种类型的整数,因此就没有必要使用特殊的语法去区别他们。
Notes | Python 2 | Python 3 |
---|---|---|
① | x = 1000000000000L |
x = 1000000000000 |
② | x = 0xFFFFFFFFFFFFL |
x = 0xFFFFFFFFFFFF |
③ | long(x) |
int(x) |
④ | type(x) is long |
type(x) is int |
⑤ | isinstance(x, long) |
isinstance(x, int) |
在Python 2里的十进制长整型在Python 3里被替换为十进制的普通整数。
在Python 2里的十六进制长整型在Python 3里被替换为十六进制的普通整数。
在Python 3里,因为长整型已经不存在了,天然原来的long()
函数也没有了。为了强制转换一个变量到整型,可使用int()
函数。
检查一个变量是不是整型,得到它的数据类型,并与一个int
类型(不是long
)的做比较。
你也可使用isinstance()
函数来检查数据类型;再强调一次,使用int
,而不是long
,来检查整数类型。
Python 2支持<>
做为!=
的同义词。Python 3只支持!=
,再也不支持<>了。
Notes | Python 2 | Python 3 |
---|---|---|
① | if x <> y: |
if x != y: |
② | if x <> y <> z: |
if x != y != z: |
简单地比较。
相对复杂的三个值之间的比较。
has_key()
#在Python 2里,字典对象的has_key()
方法用来测试字典是否包含特定的键(key)。Python 3再也不支持这个方法了。你须要使用in
运算符。
Notes | Python 2 | Python 3 |
---|---|---|
① | a_dictionary.has_key('PapayaWhip') |
'PapayaWhip' in a_dictionary |
② | a_dictionary.has_key(x) or a_dictionary.has_key(y) |
x in a_dictionary or y in a_dictionary |
③ | a_dictionary.has_key(x or y) |
(x or y) in a_dictionary |
④ | a_dictionary.has_key(x + y) |
(x + y) in a_dictionary |
⑤ | x + a_dictionary.has_key(y) |
x + (y in a_dictionary) |
最简单的形式。
运算符or
的优先级高于运算符in
,因此这里不须要添加括号。
另外一方面,出于一样的缘由 — or
的优先级大于in
,这里须要添加括号。(注意:这里的代码与前面那行彻底不一样。Python会先解释x or y
,获得结果x(若是x在布尔上下文里的值是真)或者y。而后Python检查这个结果是否是a_dictionary的一个键。)
运算符in
的优先级大于运算符+
,因此代码里的这种形式从技术上说不须要括号,可是2to3
仍是添加了。
这种形式必定须要括号,由于in
的优先级大于+
。
在Python 2里,许多字典类方法的返回值是列表。其中最经常使用方法的有keys
,items
和values
。 在Python 3里,全部以上方法的返回值改成动态视图(dynamic view)。在一些上下文环境里,这种改变并不会产生影响。若是这些方法的返回值被当即传递给另一个函数,而且那个函数会遍历整个序列,那么以上方法的 返回值是列表或者视图并不会产生什么不一样。在另一些状况下,Python 3的这些改变干系重大。若是你期待一个能被独立寻址元素的列表,那么Python 3的这些改变将会使你的代码卡住(choke),由于视图(view)不支持索引(indexing)。
Notes | Python 2 | Python 3 |
---|---|---|
① | a_dictionary.keys() |
list(a_dictionary.keys()) |
② | a_dictionary.items() |
list(a_dictionary.items()) |
③ | a_dictionary.iterkeys() |
iter(a_dictionary.keys()) |
④ | [i for i in a_dictionary.iterkeys()] |
[i for i in a_dictionary.keys()] |
⑤ | min(a_dictionary.keys()) |
no change |
使用list()
函数将keys()
的返回值转换为一个静态列表,出于安全方面的考量,2to3
可能会报错。这样的代码是有效的,可是对于使用视图来讲,它的效率低一些。你应该检查转换后的代码,看看是否必定须要列表,也许视图也能完成一样的工做。
这是另一种视图(关于items()
方法的)到列表的转换。2to3
对values()
方法返回值的转换也是同样的。
Python 3里再也不支持iterkeys()
了。若是必要,使用iter()
将keys()
的返回值转换成为一个迭代器。
2to3
可以识别出iterkeys()
方法在列表解析里被使用,而后将它转换为Python 3里的keys()
方法(不须要使用额外的iter()
去包装其返回值)。这样是可行的,由于视图是可迭代的。
2to3
也能识别出keys()
方法的返回值被当即传给另一个会遍历整个序列的函数,因此也就没有必要先把keys()
的返回值转换到一个列表。相反的,min()
函数会很乐意遍历视图。这个过程对min()
,max()
,sum()
,list()
,tuple()
,set()
,sorted()
,any()
和all()
一样有效。
从Python 2到Python 3,标准库里的一些模块已经被重命名了。还有一些相互关联的模块也被组合或者从新组织,以使得这种关联更有逻辑性。
http
#在Python 3里,几个相关的HTTP模块被组合成一个单独的包,即http
。
Notes | Python 2 | Python 3 |
---|---|---|
① | import httplib |
import http.client |
② | import Cookie |
import http.cookies |
③ | import cookielib |
import http.cookiejar |
④ | import BaseHTTPServer |
import http.server |
http.client
模块实现了一个底层的库,能够用来请求HTTP资源,解析HTTP响应。
http.cookies
模块提供一个蟒样的(Pythonic)接口来获取经过HTTP头部(HTTPheader)Set-Cookie发送的cookies
经常使用的流行的浏览器会把cookies以文件形式存放在磁盘上,http.cookiejar
模块能够操做这些文件。
http.server
模块实现了一个基本的HTTP服务器
urllib
#Python 2有一些用来分析,编码和获取URL的模块,可是这些模块就像老鼠窝同样相互重叠。在Python 3里,这些模块被重构、组合成了一个单独的包,即urllib
。
Notes | Python 2 | Python 3 |
---|---|---|
① | import urllib |
import urllib.request, urllib.parse, urllib.error |
② | import urllib2 |
import urllib.request, urllib.error |
③ | import urlparse |
import urllib.parse |
④ | import robotparser |
import urllib.robotparser |
⑤ | from urllib import FancyURLopener |
from urllib.request import FancyURLopener |
⑥ | from urllib2 import Request |
from urllib.request import Request |
之前,Python 2里的urllib
模块有各类各样的函数,包括用来获取数据的urlopen()
,还有用来将URL分割成其组成部分的splittype()
,splithost()
和splituser()
函数。在新的urllib
包里,这些函数被组织得更有逻辑性。2to3将会修改这些函数的调用以适应新的命名方案。
在Python 3里,之前的urllib2
模块被并入了urllib
包。同时,以urllib2
里各类你最喜好的东西将会一个不缺地出如今Python 3的urllib
模块里,好比build_opener()
方法,Request
对象,HTTPBasicAuthHandler
和friends。
Python 3里的urllib.parse
模块包含了原来Python 2里urlparse
模块全部的解析函数。
urllib.robotparse
模块解析robots.txt
文件。
处理HTTP重定向和其余状态码的FancyURLopener
类在Python 3里的urllib.request
模块里依然有效。urlencode()
函数已经被转移到了urllib.parse
里。
Request
对象在urllib.request
里依然有效,可是像HTTPError
这样的常量已经被转移到了urllib.error
里。
我是否有提到2to3
也会重写你的函数调用?好比,若是你的Python 2代码里导入了urllib
模块,调用了urllib.urlopen()
函数获取数据,2to3
会同时修改import
语句和函数调用。
Notes | Python 2 | Python 3 |
---|---|---|
import urllib |
import urllib.request, urllib.parse, urllib.error |
dbm
#全部的DBM克隆(DBMclone)如今在单独的一个包里,即dbm
。若是你须要其中某个特定的变体,好比GNUDBM,你能够导入dbm
包中合适的模块。
Notes | Python 2 | Python 3 |
---|---|---|
import dbm |
import dbm.ndbm |
|
import gdbm |
import dbm.gnu |
|
import dbhash |
import dbm.bsd |
|
import dumbdbm |
import dbm.dumb |
|
import anydbm |
import dbm |
xmlrpc
#XML-RPC是一个经过HTTP协议执行远程RPC调用的轻重级方法。一些XML-RPC客户端和XML-RPC服务端的实现库如今被组合到了独立的包,即xmlrpc
。
Notes | Python 2 | Python 3 |
---|---|---|
import xmlrpclib |
import xmlrpc.client |
|
import DocXMLRPCServer |
import xmlrpc.server |
Notes | Python 2 | Python 3 |
---|---|---|
① | try: |
import io |
② | try: |
import pickle |
③ | import __builtin__ |
import builtins |
④ | import copy_reg |
import copyreg |
⑤ | import Queue |
import queue |
⑥ | import SocketServer |
import socketserver |
⑦ | import ConfigParser |
import configparser |
⑧ | import repr |
import reprlib |
⑨ | import commands |
import subprocess |
在Python 2里,你一般会这样作,首先尝试把cStringIO
导入做为StringIO
的替代,若是失败了,再导入StringIO
。不要在Python 3里这样作;io
模块会帮你处理好这件事情。它会找出可用的最快实现方法,而后自动使用它。
在Python 2里,导入最快的pickle
实现也是一个与上边类似的能用方法。在Python 3里,pickle
模块会自动为你处理,因此不要再这样作。
builtins
模块包含了在整个Python语言里都会使用的全局函数,类和常量。从新定义builtins
模块里的某个函数意味着在每处都重定义了这个全局函数。这听起来很强大,可是同时也是很可怕的。
copyreg
模块为用C语言定义的用户自定义类型添加了pickle
模块的支持。
queue
模块实现一个生产者消费者队列(multi-producer, multi-consumer queue)。
socketserver
模块为实现各类socket server提供了通用基础类。
configparser
模块用来解析INI-style配置文件。
reprlib
模块从新实现了内置函数repr()
,并添加了对字符串表示被截断前长度的控制。
subprocess
模块容许你建立子进程,链接到他们的管道,而后获取他们的返回值。
包是由一组相关联的模块共同组成的单个实体。在Python 2的时候,为了实现同一个包内模块的相互引用,你会使用import foo
或者from foo import Bar
。Python 2解释器会先在当前目录里搜索foo.py
,而后再去Python搜索路径(sys.path
)里搜索。在Python 3里这个过程有一点不一样。Python 3不会首先在当前路径搜索,它会直接在Python的搜索路径里寻找。若是你想要包里的一个模块导入包里的另一个模块,你须要显式地提供两个模块的相对路径。
假设你有以下包,多个文件在同一个目录下:
chardet/ | +--__init__.py | +--constants.py | +--mbcharsetprober.py | +--universaldetector.py
如今假设universaldetector.py
须要整个导入constants.py
,另外还须要导入mbcharsetprober.py
的一个类。你会怎样作?
Notes | Python 2 | Python 3 |
---|---|---|
① | import constants |
from . import constants |
② | from mbcharsetprober import MultiByteCharSetProber |
from .mbcharsetprober import MultiByteCharsetProber |
当你须要从包的其余地方导入整个模块,使用新的from . import
语法。这里的句号(.)即表示当前文件(universaldetector.py
)和你想要导入文件(constants.py
)之间的相对路径。在这个样例中,这两个文件在同一个目录里,因此使用了单个句号。你也能够从父目录(from .. import anothermodule
)或者子目录里导入。
为了将一个特定的类或者函数从其余模块里直接导入到你的模块的名字空间里,在须要导入的模块名前加上相对路径,而且去掉最后一个斜线(slash)。在这个例子中,mbcharsetprober.py
与universaldetector.py
在同一个目录里,因此相对路径名就是一个句号。你也能够从父目录(from .. import anothermodule)或者子目录里导入。
next()
#在Python 2里,迭代器有一个next()
方法,用来返回序列里的下一项。在Python 3里这一样成立,可是如今有了一个新的全局的函数next()
,它使用一个迭代器做为参数。
Notes | Python 2 | Python 3 |
---|---|---|
① | anIterator.next() |
next(anIterator) |
② | a_function_that_returns_an_iterator().next() |
next(a_function_that_returns_an_iterator()) |
③ | class A: |
class A: |
④ | class A: |
no change |
⑤ | next = 42 |
next = 42 |
最简单的例子,你再也不调用一个迭代器的next()
方法,如今你将迭代器自身做为参数传递给全局函数next()
。
假如你有一个返回值是迭代器的函数,调用这个函数而后把结果做为参数传递给next()
函数。(2to3
脚本足够智能以正确执行这种转换。)
假如你想定义你本身的类,而后把它用做一个迭代器,在Python 3里,你能够经过定义特殊方法__next__()
来实现。
若是你定义的类里恰好有一个next()
,它使用一个或者多个参数,2to3
执行的时候不会动它。这个类不能被看成迭代器使用,由于它的next()
方法带有参数。
这一个有些复杂。若是你刚好有一个叫作next的本地变量,在Python 3里它的优先级会高于全局函数next()
。在这种状况下,你须要调用迭代器的特别方法__next__()
来获取序列里的下一个元素。(或者,你也能够重构代码以使这个本地变量的名字不叫next,可是2to3不会为你作这件事。)
filter()
#在Python 2里,filter()
方法返回一个列表,这个列表是经过一个返回值为True
或者False
的函数来检测序列里的每一项获得的。在Python 3里,filter()
函数返回一个迭代器,再也不是列表。
Notes | Python 2 | Python 3 |
---|---|---|
① | filter(a_function, a_sequence) |
list(filter(a_function, a_sequence)) |
② | list(filter(a_function, a_sequence)) |
no change |
③ | filter(None, a_sequence) |
[i for i in a_sequence if i] |
④ | for i in filter(None, a_sequence): |
no change |
⑤ | [i for i in filter(a_function, a_sequence)] |
no change |
最简单的状况下,2to3
会用一个list()
函数来包装filter()
,list()
函数会遍历它的参数而后返回一个列表。
然而,若是filter()
调用已经被list()
包裹,2to3
不会再作处理,由于这种状况下filter()
的返回值是不是一个迭代器是可有可无的。
为了处理filter(None, ...)
这种特殊的语法,2to3
会将这种调用从语法上等价地转换为列表解析。
因为for
循环会遍历整个序列,因此没有必要再作修改。
与上面相同,不须要作修改,由于列表解析会遍历整个序列,即便filter()
返回一个迭代器,它仍能像之前的filter()
返回列表那样正常工做。
map()
#跟filter()
做的改变同样,map()
函数如今返回一个迭代器。(在Python 2里,它返回一个列表。)
Notes | Python 2 | Python 3 |
---|---|---|
① | map(a_function, 'PapayaWhip') |
list(map(a_function, 'PapayaWhip')) |
② | map(None, 'PapayaWhip') |
list('PapayaWhip') |
③ | map(lambda x: x+1, range(42)) |
[x+1 for x in range(42)] |
④ | for i in map(a_function, a_sequence): |
no change |
⑤ | [i for i in map(a_function, a_sequence)] |
no change |
相似对filter()
的处理,在最简单的状况下,2to3
会用一个list()
函数来包装map()
调用。
对于特殊的map(None, ...)
语法,跟filter(None, ...)
相似,2to3
会将其转换成一个使用list()
的等价调用
若是map()
的第一个参数是一个lambda函数,2to3
会将其等价地转换成列表解析。
对于会遍历整个序列的for
循环,不须要作改变。
再一次地,这里不须要作修改,由于列表解析会遍历整个序列,即便map()
的返回值是迭代器而不是列表它也能正常工做。
reduce()
#在Python 3里,reduce()
函数已经被从全局名字空间里移除了,它如今被放置在fucntools
模块里。
Notes | Python 2 | Python 3 |
---|---|---|
reduce(a, b, c) |
from functools import reduce |
apply()
#Python 2有一个叫作apply()
的全局函数,它使用一个函数f和一个列表[a, b, c]
做为参数,返回值是f(a, b, c)
。你也能够经过直接调用这个函数,在列表前添加一个星号(*)做为参数传递给它来完成一样的事情。在Python 3里,apply()
函数再也不存在了;必须使用星号标记法。
Notes | Python 2 | Python 3 |
---|---|---|
① | apply(a_function, a_list_of_args) |
a_function(*a_list_of_args) |
② | apply(a_function, a_list_of_args, a_dictionary_of_named_args) |
a_function(*a_list_of_args, **a_dictionary_of_named_args) |
③ | apply(a_function, a_list_of_args + z) |
a_function(*a_list_of_args + z) |
④ | apply(aModule.a_function, a_list_of_args) |
aModule.a_function(*a_list_of_args) |
最简单的形式,能够经过在参数列表(就像[a, b, c]
同样)前添加一个星号来调用函数。这跟Python 2里的apply()
函数是等价的。
在Python 2里,apply()
函数实际上能够带3个参数:一个函数,一个参数列表,一个字典命名参数(dictionary of named arguments)。在Python 3里,你能够经过在参数列表前添加一个星号(*
),在字典命名参数前添加两个星号(**
)来达到一样的效果。
运算符+
在这里用做链接列表的功能,它的优先级高于运算符*
,因此没有必要在a_list_of_args + z
周围添加额外的括号。
2to3
脚本足够智能来转换复杂的apply()
调用,包括调用导入模块里的函数。
intern()
#在Python 2里,你能够用intern()
函数做用在一个字符串上来限定(intern)它以达到性能优化。在Python 3里,intern()
函数被转移到sys
模块里了。
Notes | Python 2 | Python 3 |
---|---|---|
intern(aString) |
sys.intern(aString) |
exec
语句#就像print
语句在Python 3里变成了一个函数同样,exec
语句也是这样的。exec()
函数使用一个包含任意Python代码的字符串做为参数,而后就像执行语句或者表达式同样执行它。exec()
跟eval()
是类似的,可是exec()
更增强大并更具备技巧性。eval()
函数只能执行单独一条表达式,可是
可以执行多条语句,导入(import),函数声明 — 实际上整个Python程序的字符串表示也能够。exec
()
Notes | Python 2 | Python 3 |
---|---|---|
① | exec codeString |
exec(codeString) |
② | exec codeString in a_global_namespace |
exec(codeString, a_global_namespace) |
③ | exec codeString in a_global_namespace, a_local_namespace |
exec(codeString, a_global_namespace, a_local_namespace) |
在最简单的形式下,由于exec()
如今是一个函数,而不是语句,2to3
会把这个字符串形式的代码用括号围起来。
Python 2里的exec
语句能够指定名字空间,代码将在这个由全局对象组成的私有空间里执行。Python 3也有这样的功能;你只须要把这个名字空间做为第二个参数传递给exec()
函数。
更加神奇的是,Python 2里的exec
语句还能够指定一个本地名字空间(好比一个函数里声明的变量)。在Python 3里,exec()
函数也有这样的功能。
execfile
语句#就像之前的exec
语句,Python 2里的execfile
语句也能够像执行Python代码那样使用字符串。不一样的是exec
使用字符串,而execfile
则使用文件。在Python 3里,execfile
语句已经被去掉了。若是你真的想要执行一个文件里的Python代码(可是你不想导入它),你能够经过打开这个文件,读取它的内容,而后调用compile()
全局函数强制Python解释器编译代码,而后调用新的exec()
函数。
Notes | Python 2 | Python 3 |
---|---|---|
execfile('a_filename') |
exec(compile(open('a_filename').read(), 'a_filename', 'exec')) |
repr
(反引号)#在Python 2里,为了获得一个任意对象的字符串表示,有一种把对象包装在反引号里(好比`x`
)的特殊语法。在Python 3里,这种能力仍然存在,可是你不能再使用反引号得到这种字符串表示了。你须要使用全局函数repr()
。
Notes | Python 2 | Python 3 |
---|---|---|
① | `x` |
repr(x) |
② | `'PapayaWhip' + `2`` |
repr('PapayaWhip' + repr(2)) |
记住,x能够是任何东西 — 一个类,函数,模块,基本数据类型,等等。repr()
函数可使用任何类型的参数。
在Python 2里,反引号能够嵌套,致使了这种使人费解的(可是有效的)表达式。2to3
足够智能以将这种嵌套调用转换到repr()
函数。
try...except
语句#从Python 2到Python 3,捕获异常的语法有些许变化。
Notes | Python 2 | Python 3 |
---|---|---|
① | try: |
try: |
② | try: |
try: |
③ | try: |
no change |
④ | try: |
no change |
相对于Python 2里在异常类型后添加逗号,Python 3使用了一个新的关键字,as
。
关键字as
也能够用在一次捕获多种类型异常的状况下。
若是你捕获到一个异常,可是并不在乎访问异常对象自己,Python 2和Python 3的语法是同样的。
相似地,若是你使用一个保险方法(fallback)来捕获全部异常,Python 2和Python 3的语法是同样的。
☞在导入模块(或者其余大多数状况)的时候,你绝对不该该使用这种方法(指以上的fallback)。否则的话,程序可能会捕获到像
KeyboardInterrupt
(若是用户按Ctrl-C来中断程序)这样的异常,从而使调试变得更加困难。
raise
语句#Python 3里,抛出自定义异常的语法有细微的变化。
Notes | Python 2 | Python 3 |
---|---|---|
① | raise MyException |
unchanged |
② | raise MyException, 'error message' |
raise MyException('error message') |
③ | raise MyException, 'error message', a_traceback |
raise MyException('error message').with_traceback(a_traceback) |
④ | raise 'error message' |
unsupported |
抛出不带用户自定义错误信息的异常,这种最简单的形式下,语法没有改变。
当你想要抛出一个带用户自定义错误信息的异常时,改变就显而易见了。Python 2用一个逗号来分隔异常类和错误信息;Python 3把错误信息做为参数传递给异常类。
Python 2支持一种更加复杂的语法来抛出一个带用户自定义回溯(stack trace,堆栈追踪)的异常。在Python 3里你也能够这样作,可是语法彻底不一样。
在Python 2里,你能够抛出一个不带异常类的异常,仅仅只有一个异常信息。在Python 3里,这种形式再也不被支持。2to3
将会警告你它不能自动修复这种语法。
throw
方法#在Python 2里,生成器有一个throw()
方法。调用a_generator.throw()
会在生成器被暂停的时候抛出一个异常,而后返回由生成器函数获取的下一个值。在Python 3里,这种功能仍然可用,可是语法上有一点不一样。
Notes | Python 2 | Python 3 |
---|---|---|
① | a_generator.throw(MyException) |
no change |
② | a_generator.throw(MyException, 'error message') |
a_generator.throw(MyException('error message')) |
③ | a_generator.throw('error message') |
unsupported |
最简单的形式下,生成器抛出不带用户自定义错误信息的异常。这种状况下,从Python 2到Python 3语法上没有变化 。
若是生成器抛出一个带用户自定义错误信息的异常,你须要将这个错误信息字符串(error string)传递给异常类来以实例化它。
Python 2还支持抛出只有异常信息的异常。Python 3不支持这种语法,而且2to3
会显示一个警告信息,告诉你须要手动地来修复这处代码。
xrange()
#在Python 2里,有两种方法来得到必定范围内的数字:range()
,它返回一个列表,还有range()
,它返回一个迭代器。在Python 3里,range()
返回迭代器,xrange()
再也不存在了。
Notes | Python 2 | Python 3 |
---|---|---|
① | xrange(10) |
range(10) |
② | a_list = range(10) |
a_list = list(range(10)) |
③ | [i for i in xrange(10)] |
[i for i in range(10)] |
④ | for i in range(10): |
no change |
⑤ | sum(range(10)) |
no change |
在最简单的状况下,2to3
会简单地把xrange()
转换为range()
。
若是你的Python 2代码使用range()
,2to3
不知道你是否须要一个列表,或者是否一个迭代器也行。出于谨慎,2to3
可能会报错,而后使用list()
把range()
的返回值强制转换为列表类型。
若是在列表解析里有xrange()
函数,就没有必要将其返回值转换为一个列表,由于列表解析对迭代器一样有效。
相似的,for
循环也能做用于迭代器,因此这里也没有改变任何东西。
函数sum()
能做用于迭代器,因此2to3
也没有在这里作出修改。就像返回值为视图(view)而再也不是列表的字典类方法同样,这一样适用于min()
,max()
,sum()
,list(),tuple()
,set()
,sorted()
,any()
,all()
。
raw_input()
和input()
#Python 2有两个全局函数,用来在命令行请求用户输入。第一个叫作input()
,它等待用户输入一个Python表达式(而后返回结果)。第二个叫作raw_input()
,用户输入什么它就返回什么。这让初学者很是困惑,而且这被普遍地看做是Python语言的一个“肉赘”(wart)。Python 3经过重命名raw_input()
为input()
,从而切掉了这个肉赘,因此如今的input()
就像每一个人最初期待的那样工做。
Notes | Python 2 | Python 3 |
---|---|---|
① | raw_input() |
input() |
② | raw_input('prompt') |
input('prompt') |
③ | input() |
eval(input()) |
最简单的形式,raw_input()
被替换成input()
。
在Python 2里,raw_input()
函数能够指定一个提示符做为参数。Python 3里保留了这个功能。
若是你真的想要请求用户输入一个Python表达式,计算结果,能够经过调用input()
函数而后把返回值传递给eval()
。
func_*
#在Python 2里,函数的里的代码能够访问到函数自己的特殊属性。在Python 3里,为了一致性,这些特殊属性被从新命名了。
Notes | Python 2 | Python 3 |
---|---|---|
① | a_function.func_name |
a_function.__name__ |
② | a_function.func_doc |
a_function.__doc__ |
③ | a_function.func_defaults |
a_function.__defaults__ |
④ | a_function.func_dict |
a_function.__dict__ |
⑤ | a_function.func_closure |
a_function.__closure__ |
⑥ | a_function.func_globals |
a_function.__globals__ |
⑦ | a_function.func_code |
a_function.__code__ |
__name__
属性(原func_name
)包含了函数的名字。
__doc__
属性(原funcdoc
)包含了你在函数源代码里定义的文档字符串(docstring)
__defaults__
属性(原func_defaults
)是一个保存参数默认值的元组。
__dict__
属性(原func_dict
)是一个支持任意函数属性的名字空间。
__closure__
属性(原func_closure
)是一个由cell对象组成的元组,它包含了函数对自由变量(free variable)的绑定。
__globals__
属性(原func_globals
)是一个对模块全局名字空间的引用,函数自己在这个名字空间里被定义。
__code__
属性(原func_code
)是一个代码对象,表示编译后的函数体。
xreadlines()
#在Python 2里,文件对象有一个xreadlines()
方法,它返回一个迭代器,一次读取文件的一行。这在for
循环中尤为有用。事实上,后来的Python 2版本给文件对象自己添加了这样的功能。
在Python 3里,xreadlines()
方法再也不可用了。2to3
能够解决简单的状况,可是一些边缘案例则须要人工介入。
Notes | Python 2 | Python 3 |
---|---|---|
① | for line in a_file.xreadlines(): |
for line in a_file: |
② | for line in a_file.xreadlines(5): |
no change (broken) |
若是你之前调用没有参数的xreadlines()
,2to3
会把它转换成文件对象自己。在Python 3里,这种转换后的代码能够完成前一样的工做:一次读取文件的一行,而后执行for
循环的循环体。
若是你之前使用一个参数(每次读取的行数)调用xreadlines()
,2to3
不能为你完成从Python 2到Python 3的转换,你的代码会以这样的方式失败:AttributeError: '_io.TextIOWrapper' object has no attribute 'xreadlines'
。你能够手工的把xreadlines()
改为readlines()
以使代码能在Python 3下工做。(readline()方法在Python 3里返回迭代器,因此它跟Python 2里的xreadlines()
效率是不相上下的。)
☃
lambda
函数#在Python 2里,你能够定义匿名lambda
函数(anonymous lambda
function),经过指定做为参数的元组的元素个数,使这个函数实际上可以接收多个参数。事实上,Python 2的解释器把这个元组“解开”(unpack)成命名参数(named arguments),而后你能够在lambda
函数里引用它们(经过名字)。在Python 3里,你仍然能够传递一个元组做为lambda
函数的参数,可是Python解释器不会把它解析成命名参数。你须要经过位置索引(positional index)来引用每一个参数。
Notes | Python 2 | Python 3 |
---|---|---|
① | lambda (x,): x + f(x) |
lambda x1: x1[0] + f(x1[0]) |
② | lambda (x, y): x + f(y) |
lambda x_y: x_y[0] + f(x_y[1]) |
③ | lambda (x, (y, z)): x + y + z |
lambda x_y_z: x_y_z[0] + x_y_z[1][0] + x_y_z[1][1] |
④ | lambda x, y, z: x + y + z |
unchanged |
若是你已经定义了一个lambda
函数,它使用包含一个元素的元组做为参数,在Python 3里,它会被转换成一个包含到x1[0]的引用的lambda
函数。x1是2to3
脚本基于原来元组里的命名参数自动生成的。
使用含有两个元素的元组(x, y)做为参数的lambda
函数被转换为x_y,它有两个位置参数,即x_y[0]和x_y[1]。
2to3
脚本甚至能够处理使用嵌套命名参数的元组做为参数的lambda
函数。产生的结果代码有点难以阅读,可是它在Python 3下跟原来的代码在Python 2下的效果是同样的。
你能够定义使用多个参数的lambda
函数。若是没有括号包围在参数周围,Python 2会把它看成一个包含多个参数的lambda
函数;在这个lambda
函数体里,你经过名字引用这些参数,就像在其余类型的函数里所作的同样。这种语法在Python 3里仍然有效。
在Python 2里,类方法能够访问到定义他们的类对象(class object),也能访问方法对象(method object)自己。im_self
是类的实例对象;im_func
是函数对象,im_class
是类自己。在Python 3里,这些属性被从新命名,以遵循其余属性的命名约定。
Notes | Python 2 | Python 3 |
---|---|---|
aClassInstance.aClassMethod.im_func |
aClassInstance.aClassMethod.__func__ |
|
aClassInstance.aClassMethod.im_self |
aClassInstance.aClassMethod.__self__ |
|
aClassInstance.aClassMethod.im_class |
aClassInstance.aClassMethod.__self__.__class__ |
__nonzero__
特殊方法#在Python 2里,你能够建立本身的类,并使他们可以在布尔上下文(boolean context)中使用。举例来讲,你能够实例化这个类,并把这个实例对象用在一个if
语句中。为了实现这个目的,你定义一个特别的__nonzero__()
方法,它的返回值为True
或者False
,当实例对象处在布尔上下文中的时候这个方法就会被调用 。在Python 3里,你仍然能够完成一样的功能,可是这个特殊方法的名字变成了__bool__()
。
Notes | Python 2 | Python 3 |
---|---|---|
① | class A: |
class A: |
② | class A: |
no change |
当在布尔上下文使用一个类对象时,Python 3会调用__bool__()
,而非__nonzero__()
。
然而,若是你有定义了一个使用两个参数的__nonzero__()
方法,2to3
脚本会假设你定义的这个方法有其余用处,所以不会对代码作修改。
在Python 2和Python 3之间,定义八进制(octal)数的语法有轻微的改变。
Notes | Python 2 | Python 3 |
---|---|---|
x = 0755 |
x = 0o755 |
sys.maxint
#因为长整型和整型被整合在一块儿了,sys.maxint
常量再也不精确。可是由于这个值对于检测特定平台的能力仍是有用处的,因此它被Python 3保留,而且重命名为sys.maxsize
。
Notes | Python 2 | Python 3 |
---|---|---|
① | from sys import maxint |
from sys import maxsize |
② | a_function(sys.maxint) |
a_function(sys.maxsize) |
maxint
变成了maxsize
。
全部的sys.maxint
都变成了sys.maxsize
。
callable()
#在Python 2里,你可使用全局函数callable()
来检查一个对象是否可调用(callable,好比函数)。在Python 3里,这个全局函数被取消了。为了检查一个对象是否可调用,能够检查特殊方法__call__()
的存在性。
Notes | Python 2 | Python 3 |
---|---|---|
callable(anything) |
hasattr(anything, '__call__') |
zip()
#在Python 2里,全局函数zip()
可使用任意多个序列做为参数,它返回一个由元组构成的列表。第一个元组包含了每一个序列的第一个元素;第二个元组包含了每一个序列的第二个元素;依次递推下去。在Python 3里,zip()
返回一个迭代器,而非列表。
Notes | Python 2 | Python 3 |
---|---|---|
① | zip(a, b, c) |
list(zip(a, b, c)) |
② | d.join(zip(a, b, c)) |
no change |
最简单的形式,你能够经过调用list()
函数包装zip()
的返回值来恢复zip()
函数之前的功能,list()
函数会遍历这个zip()
函数返回的迭代器,而后返回结果的列表表示。
在已经会遍历序列全部元素的上下文环境里(好比这里对join()
方法的调用),zip()
返回的迭代器可以正常工做。2to3
脚本会检测到这些状况,不会对你的代码做出改变。
StandardError
异常#在Python 2里,StandardError
是除了StopIteration
,GeneratorExit
,KeyboardInterrupt
,SystemExit
以外全部其余内置异常的基类。在Python 3里,StandardError
已经被取消了;使用Exception
替代。
Notes | Python 2 | Python 3 |
---|---|---|
x = StandardError() |
x = Exception() |
|
x = StandardError(a, b, c) |
x = Exception(a, b, c) |
types
模块中的常量#types
模块里各类各样的常量能帮助你决定一个对象的类型。在Python 2里,它包含了表明全部基本数据类型的常量,如dict
和int
。在Python 3里,这些常量被已经取消了。只须要使用基础类型的名字来替代。
Notes | Python 2 | Python 3 |
---|---|---|
types.UnicodeType |
str |
|
types.StringType |
bytes |
|
types.DictType |
dict |
|
types.IntType |
int |
|
types.LongType |
int |
|
types.ListType |
list |
|
types.NoneType |
type(None) |
|
types.BooleanType |
bool |
|
types.BufferType |
memoryview |
|
types.ClassType |
type |
|
types.ComplexType |
complex |
|
types.EllipsisType |
type(Ellipsis) |
|
types.FloatType |
float |
|
types.ObjectType |
object |
|
types.NotImplementedType |
type(NotImplemented) |
|
types.SliceType |
slice |
|
types.TupleType |
tuple |
|
types.TypeType |
type |
|
types.XRangeType |
range |
☞
types.StringType
被映射为bytes
,而非str
,由于Python 2里的“string”(非Unicode编码的字符串,即普通字符串)事实上只是一些使用某种字符编码的字节序列(a sequence of bytes)。
isinstance()
#isinstance()
函数检查一个对象是不是一个特定类(class)或者类型(type)的实例。在Python 2里,你能够传递一个由类型(types)构成的元组给isinstance()
,若是该对象是元组里的任意一种类型,函数返回True
。在Python 3里,你依然能够这样作,可是不推荐使用把一种类型做为参数传递两次。
Notes | Python 2 | Python 3 |
---|---|---|
isinstance(x, (int, float, int)) |
isinstance(x, (int, float)) |
basestring
数据类型#Python 2有两种字符串类型:Unicode编码的字符串和非Unicode编码的字符串。可是其实还有另外 一种类型,即basestring
。它是一个抽象数据类型,是str
和unicode
类型的超类(superclass)。它不能被直接调用或者实例化,可是你能够把它做为isinstance()
的参数来检测一个对象是不是一个Unicode字符串或者非Unicode字符串。在Python 3里,只有一种字符串类型,因此basestring
就没有必要再存在了。
Notes | Python 2 | Python 3 |
---|---|---|
isinstance(x, basestring) |
isinstance(x, str) |
itertools
模块#Python 2.3引入了itertools
模块,它定义了全局函数zip()
,map()
,filter()
的变体(variant),这些变体的返回类型为迭代器,而非列表。在Python 3里,因为这些全局函数的返回类型原本就是迭代器,因此这些itertools
里的这些变体函数就被取消了。(在itertools
模块里仍然还有许多其余的有用的函数,而不只仅是以上列出的这些。)
Notes | Python 2 | Python 3 |
---|---|---|
① | itertools.izip(a, b) |
zip(a, b) |
② | itertools.imap(a, b) |
map(a, b) |
③ | itertools.ifilter(a, b) |
filter(a, b) |
④ | from itertools import imap, izip, foo |
from itertools import foo |
使用全局的zip()
函数,而非itertools.izip()
。
使用map()
而非itertools.imap()
。
itertools.ifilter()
变成了filter()
。
itertools
模块在Python 3里仍然存在,它只是再也不包含那些已经转移到全局名字空间的函数。2to3
脚本可以足够智能地去移除那些再也不有用的导入语句,同时保持其余的导入语句的完整性。
sys.exc_type
, sys.exc_value
, sys.exc_traceback
#处理异常的时候,在sys
模块里有三个你能够访问的变量:sys.exc_type,
sys.exc_value,
sys.exc_traceback
。(实际上这些在Python 1的时代就有。)从Python 1.5开始,因为新出的sys.exc_info
,再也不推荐使用这三个变量了,这是一个包含全部以上三个元素的元组。在Python 3里,这三个变量终于再也不存在了;这意味着,你必须使用sys.exc_info
。
Notes | Python 2 | Python 3 |
---|---|---|
sys.exc_type |
sys.exc_info()[0] |
|
sys.exc_value |
sys.exc_info()[1] |
|
sys.exc_traceback |
sys.exc_info()[2] |
对元组的列表解析#
在Python 2里,若是你须要编写一个遍历元组的列表解析,你不须要在元组值的周围加上括号。在Python 3里,这些括号是必需的。
Notes | Python 2 | Python 3 |
---|---|---|
[i for i in 1, 2] |
[i for i in (1, 2)] |
os.getcwdu()
函数#
Python 2有一个叫作
os.getcwd()
的函数,它将当前的工做目录做为一个(非Unicode编码的)字符串返回。因为现代的文件系统可以处理能何字符编码的目录名,Python 2.3引入了os.getcwdu()
函数。os.getcwdu()
函数把当前工做目录用Unicode编码的字符串返回。在Python 3里,因为只有一种字符串类型(Unicode类型的),因此你只须要os.getcwd()
就能够了。
Notes | Python 2 | Python 3 |
---|---|---|
os.getcwdu() |
os.getcwd() |
元类(metaclass)#
在Python 2里,你能够经过在类的声明中定义
metaclass
参数,或者定义一个特殊的类级别的(class-level)__metaclass__
属性,来建立元类。在Python 3里,__metaclass__
属性已经被取消了。
Notes | Python 2 | Python 3 |
---|---|---|
① | class C(metaclass=PapayaMeta): |
unchanged |
② | class Whip: |
class Whip(metaclass=PapayaMeta): |
③ | class C(Whipper, Beater): |
class C(Whipper, Beater, metaclass=PapayaMeta): |
在声明类的时候声明
metaclass
参数,这在Python 2和Python 3里都有效,它们是同样的。
在类的定义里声明
__metaclass__
属性在Python 2里有效,可是在Python 3里再也不有效。
2to3
可以构建一个有效的类声明,即便这个类继承自多个父类。
关于代码风格#
如下所列的“修补”(fixes)实质上并不算真正的修补。意思就是,他们只是代码的风格上的事情,而不涉及到代码的本质。可是Python的开发者们在使得代码风格尽量一致方面很是有兴趣(have a vested interest)。为此,有一个专门o描述Python代码风格的官方指导手册 — 细致到能令人痛苦 — 都是一些你不太可能关心的在各类各样的细节上的挑剔。鉴于
2to3
为转换代码提供了一个这么好的条件,脚本的做者们添加了一些可选的特性以使你的代码更具可读性。
set()
字面值(literal)(显式的)#
在Python 2城,定义一个字面值集合(literal set)的惟一方法就是调用
set(a_sequence)
。在Python 3里这仍然有效,可是使用新的标注记号(literal notation):大括号({})是一种更清晰的方法。这种方法除了空集之外都有效,由于字典也用大括号标记,因此{}
表示一个空的字典,而不是一个空集。
☞
2to3
脚本默认不会修复set()
字面值。为了开启这个功能,在命令行调用2to3
的时候指定-f set_literal参数。
Notes | Before | After |
---|---|---|
set([1, 2, 3]) |
{1, 2, 3} |
|
set((1, 2, 3)) |
{1, 2, 3} |
|
set([i for i in a_sequence]) |
{i for i in a_sequence} |
全局函数buffer()
(显式的)#
用C实现的Python对象能够导出一个“缓冲区接口”(buffer interface),它容许其余的Python代码直接读写一块内存。(这听起来很强大,它也一样可怕。)在Python 3里,
buffer()
被从新命名为memoryview()
。(实际的修改更加复杂,可是你几乎能够忽略掉这些不一样之处。)
☞
2to3
脚本默认不会修复buffer()
函数。为了开启这个功能,在命令行调用2to3
的时候指定-f buffer参数。
Notes | Before | After |
---|---|---|
x = buffer(y) |
x = memoryview(y) |
逗号周围的空格(显式的)#
尽管Python对用于缩进和凸出(indenting and outdenting)的空格要求很严格,可是对于空格在其余方面的使用Python仍是很自由的。在列表,元组,集合和字典里,空格能够出如今逗号的前 面或者后面,这不会有什么坏影响。可是,Python代码风格指导手册上指出,逗号前不能有空格,逗号后应该包含一个空格。尽管这纯粹只是一个美观上的考 量(代码仍然能够正常工做,在Python 2和Python 3里均可以),可是
2to3
脚本能够依据手册上的标准为你完成这个修复。
☞
2to3
脚本默认不会修复逗号周围的空格。为了开启这个功能,在命令行调用2to3
的时候指定-f wscomma参数。
Notes | Before | After |
---|---|---|
a ,b |
a, b |
|
{a :b} |
{a: b} |
惯例(Common idioms)(显式的)#
在Python社区里创建起来了许多惯例。有一些好比
while 1:
loop,它能够追溯到Python 1。(Python直到Python 2.3才有真正意义上的布尔类型,因此开发者之前使用1
和0
替代。)当代的Python程序员应该锻炼他们的大脑以使用这些惯例的现代版。
☞
2to3
脚本默认不会为这些惯例作修复。为了开启这个功能,在命令行调用2to3
的时候指定-f idioms参数。
Notes | Before | After |
---|---|---|
while 1: |
while True: |
|
type(x) == T |
isinstance(x, T) |
|
type(x) is T |
isinstance(x, T) |
|
a_list = list(a_sequence) |
a_list = sorted(a_sequence)do_stuff(a_list) |