返回列表的字典类方法#
在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 iin a_dictionary.iterkeys()] | [i for iin 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 http.server |
http.client
模块实现了一个底层的库,可以用来请求HTTP资源,解析HTTP响应。http.cookies
模块提供一个蟒样的(Pythonic)接口来获取通过HTTP头部(HTTP header)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 |
⑤ |
|
|
⑥ |
|
|
以前,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 |
---|---|---|
|
|
dbm
#
所有的DBM克隆(DBM clone)现在在单独的一个包里,即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 dbm |
xmlrpc
#
XML-RPC是一个通过HTTP协议执行远程RPC调用的轻重级方法。一些XML-RPC客户端和XML-RPC服务端的实现库现在被组合到了独立的包,即xmlrpc
。
Notes | Python 2 | Python 3 |
---|---|---|
import xmlrpclib | import xmlrpc.client | |
| import xmlrpc.server |
其他模块#
Notes | Python 2 | Python 3 |
---|---|---|
① |
| import io |
② |
| 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 mbcharsetproberimportMultiByteCharSetProber | from .mbcharsetproberimportMultiByteCharsetProber |
当你需要从包的其他地方导入整个模块,使用新的
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()) |
③ |
|
|
④ |
| no change |
⑤ |
|
|
最简单的例子,你不再调用一个迭代器的
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 iin a_sequence if i] |
④ | for i in filter(None, a_sequence): | no change |
⑤ | [i for iin 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+1for x in range(42)] |
④ | for i in map(a_function, a_sequence): | no change |
⑤ | [i for iin 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) |
|
全局函数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 |
---|---|---|
① |
|
|
② |
|
|
③ |
| no change |
④ |
| 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
将会警告你它不能自动修复这种语法。