Python3和2常用语句的语法区别2.0

返回列表的字典类方法#

在Python 2里,许多字典类方法的返回值是列表。其中最常用方法的有keysitemsvalues。在Python 3里,所有以上方法的返回值改为动态视图(dynamic view)。在一些上下文环境里,这种改变并不会产生影响。如果这些方法的返回值被立即传递给另外一个函数,并且那个函数会遍历整个序列,那么以上方法的返回值是列表或者视图并不会产生什么不同。在另外一些情况下,Python 3的这些改变干系重大。如果你期待一个能被独立寻址元素的列表,那么Python 3的这些改变将会使你的代码卡住(choke),因为视图(view)不支持索引(indexing)。

NotesPython 2Python 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
  1. 使用list()函数将keys()的返回值转换为一个静态列表,出于安全方面的考量,2to3可能会报错。这样的代码是有效的,但是对于使用视图来说,它的效率低一些。你应该检查转换后的代码,看看是否一定需要列表,也许视图也能完成同样的工作。

  2. 这是另外一种视图(关于items()方法的)到列表的转换。2to3values()方法返回值的转换也是一样的。

  3. Python 3里不再支持iterkeys()了。如果必要,使用iter()keys()的返回值转换成为一个迭代器。

  4. 2to3能够识别出iterkeys()方法在列表解析里被使用,然后将它转换为Python 3里的keys()方法(不需要使用额外的iter()去包装其返回值)。这样是可行的,因为视图是可迭代的。

  5. 2to3也能识别出keys()方法的返回值被立即传给另外一个会遍历整个序列的函数,所以也就没有必要先把keys()的返回值转换到一个列表。相反的,min()函数会很乐意遍历视图。这个过程对min()max()sum()list()tuple()set()sorted()any()all()同样有效。

被重命名或者重新组织的模块#

从Python 2到Python 3,标准库里的一些模块已经被重命名了。还有一些相互关联的模块也被组合或者重新组织,以使得这种关联更有逻辑性。

http#

在Python 3里,几个相关的HTTP模块被组合成一个单独的包,即http

NotesPython 2Python 3
import httplibimport http.client
import Cookieimport http.cookies
import cookielibimport http.cookiejar
import BaseHTTPServerimport SimpleHTTPServerimport CGIHttpServer
import http.server
  1. http.client模块实现了一个底层的库,可以用来请求HTTP资源,解析HTTP响应。

  2. http.cookies模块提供一个蟒样的(Pythonic)接口来获取通过HTTP头部(HTTP header)Set-Cookie发送的cookies

  3. 常用的流行的浏览器会把cookies以文件形式存放在磁盘上,http.cookiejar模块可以操作这些文件。

  4. http.server模块实现了一个基本的HTTP服务器

urllib#

Python 2有一些用来分析,编码和获取URL的模块,但是这些模块就像老鼠窝一样相互重叠。在Python 3里,这些模块被重构、组合成了一个单独的包,即urllib

NotesPython 2Python 3
import urllibimport urllib.request, urllib.parse, urllib.error
import urllib2import urllib.request, urllib.error
import urlparseimport urllib.parse
import robotparserimport urllib.robotparser
from urllib import FancyURLopenerfrom urllib import urlencode
from urllib.request import FancyURLopenerfrom urllib.parse import urlencode
from urllib2 import Requestfrom urllib2 import HTTPError
from urllib.request import Requestfrom urllib.error import HTTPError
  1. 以前,Python 2里的urllib模块有各种各样的函数,包括用来获取数据的urlopen(),还有用来将URL分割成其组成部分的splittype()splithost()splituser()函数。在新的urllib包里,这些函数被组织得更有逻辑性。2to3将会修改这些函数的调用以适应新的命名方案。

  2. 在Python 3里,以前的urllib2模块被并入了urllib包。同时,以urllib2里各种你最喜爱的东西将会一个不缺地出现在Python 3的urllib模块里,比如build_opener()方法,Request对象,HTTPBasicAuthHandler和friends。

  3. Python 3里的urllib.parse模块包含了原来Python 2里urlparse模块所有的解析函数。

  4. urllib.robotparse模块解析robots.txt文件

  5. 处理HTTP重定向和其他状态码的FancyURLopener类在Python 3里的urllib.request模块里依然有效。urlencode()函数已经被转移到了urllib.parse里。

  6. Request对象在urllib.request里依然有效,但是像HTTPError这样的常量已经被转移到了urllib.error里。

我是否有提到2to3也会重写你的函数调用?比如,如果你的Python 2代码里导入了urllib模块,调用了urllib.urlopen()函数获取数据,2to3会同时修改import语句和函数调用。

NotesPython 2Python 3
 
import urllibprint urllib.urlopen('http://diveintopython3.org/').read()
import urllib.request, urllib.parse, urllib.errorprint(urllib.request.urlopen('http://diveintopython3.org/').read())

dbm#

所有的DBM克隆(DBM clone)现在在单独的一个包里,即dbm。如果你需要其中某个特定的变体,比如GNUDBM,你可以导入dbm包中合适的模块。

NotesPython 2Python 3
 import dbmimport dbm.ndbm
 import gdbmimport dbm.gnu
 import dbhashimport dbm.bsd
 import dumbdbmimport dbm.dumb
 
import anydbmimport whichdb
import dbm

xmlrpc#

XML-RPC是一个通过HTTP协议执行远程RPC调用的轻重级方法。一些XML-RPC客户端和XML-RPC服务端的实现库现在被组合到了独立的包,即xmlrpc

NotesPython 2Python 3
 import xmlrpclibimport xmlrpc.client
 
import DocXMLRPCServerimport SimpleXMLRPCServer
import xmlrpc.server

其他模块#

NotesPython 2Python 3
try:    import cStringIO as StringIOexcept ImportError:    import StringIO
import io
try:    import cPickle as pickleexcept ImportError:    import pickle
import pickle
import __builtin__import builtins
import copy_regimport copyreg
import Queueimport queue
import SocketServerimport socketserver
import ConfigParserimport configparser
import reprimport reprlib
import commandsimport subprocess
  1. 在Python 2里,你通常会这样做,首先尝试把cStringIO导入作为StringIO的替代,如果失败了,再导入StringIO。不要在Python 3里这样做;io模块会帮你处理好这件事情。它会找出可用的最快实现方法,然后自动使用它。

  2. 在Python 2里,导入最快的pickle实现也是一个与上边相似的能用方法。在Python 3里,pickle模块会自动为你处理,所以不要再这样做。

  3. builtins模块包含了在整个Python语言里都会使用的全局函数,类和常量。重新定义builtins模块里的某个函数意味着在每处都重定义了这个全局函数。这听起来很强大,但是同时也是很可怕的。

  4. copyreg模块为用C语言定义的用户自定义类型添加了pickle模块的支持。

  5. queue模块实现一个生产者消费者队列(multi-producer, multi-consumer queue)。

  6. socketserver模块为实现各种socket server提供了通用基础类。

  7. configparser模块用来解析INI-style配置文件。

  8. reprlib模块重新实现了内置函数repr(),并添加了对字符串表示被截断前长度的控制。

  9. 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的一个类。你会怎样做?

NotesPython 2Python 3
import constantsfrom .import constants
from mbcharsetproberimportMultiByteCharSetProberfrom .mbcharsetproberimportMultiByteCharsetProber
  1. 当你需要从包的其他地方导入整个模块,使用新的from . import语法。这里的句号(.)即表示当前文件(universaldetector.py)和你想要导入文件(constants.py)之间的相对路径。在这个样例中,这两个文件在同一个目录里,所以使用了单个句号。你也可以从父目录(from .. import anothermodule)或者子目录里导入。

  2. 为了将一个特定的类或者函数从其他模块里直接导入到你的模块的名字空间里,在需要导入的模块名前加上相对路径,并且去掉最后一个斜线(slash)。在这个例子中,mbcharsetprober.pyuniversaldetector.py在同一个目录里,所以相对路径名就是一个句号。你也可以从父目录(from .. import anothermodule)或者子目录里导入。

迭代器方法next()#

在Python 2里,迭代器有一个next()方法,用来返回序列里的下一项。在Python 3里这同样成立,但是现在有了一个新的全局的函数next(),它使用一个迭代器作为参数。

NotesPython 2Python 3
anIterator.next()next(anIterator)
a_function_that_returns_an_iterator().next()next(a_function_that_returns_an_iterator())
class A:    def next(self):        pass
class A:    def __next__(self):        pass
class A:    def next(self, x, y):        pass
no change
next = 42for an_iterator in a_sequence_of_iterators:    an_iterator.next()
next = 42for an_iterator in a_sequence_of_iterators:    an_iterator.__next__()
  1. 最简单的例子,你不再调用一个迭代器的next()方法,现在你将迭代器自身作为参数传递给全局函数next()

  2. 假如你有一个返回值是迭代器的函数,调用这个函数然后把结果作为参数传递给next()函数。(2to3脚本足够智能以正确执行这种转换。)

  3. 假如你想定义你自己的类,然后把它用作一个迭代器,在Python 3里,你可以通过定义特殊方法__next__()来实现。

  4. 如果你定义的类里刚好有一个next(),它使用一个或者多个参数,2to3执行的时候不会动它。这个类不能被当作迭代器使用,因为它的next()方法带有参数。

  5. 这一个有些复杂。如果你恰好有一个叫做next的本地变量,在Python 3里它的优先级会高于全局函数next()。在这种情况下,你需要调用迭代器的特别方法__next__()来获取序列里的下一个元素。(或者,你也可以重构代码以使这个本地变量的名字不叫next,但是2to3不会为你做这件事。)

全局函数filter()#

在Python 2里,filter()方法返回一个列表,这个列表是通过一个返回值为True或者False的函数来检测序列里的每一项得到的。在Python 3里,filter()函数返回一个迭代器,不再是列表。

NotesPython 2Python 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
  1. 最简单的情况下,2to3会用一个list()函数来包装filter()list()函数会遍历它的参数然后返回一个列表。

  2. 然而,如果filter()调用已经被list()包裹,2to3不会再做处理,因为这种情况下filter()的返回值是否是一个迭代器是无关紧要的。

  3. 为了处理filter(None, ...)这种特殊的语法,2to3会将这种调用从语法上等价地转换为列表解析。

  4. 由于for循环会遍历整个序列,所以没有必要再做修改。

  5. 与上面相同,不需要做修改,因为列表解析会遍历整个序列,即使filter()返回一个迭代器,它仍能像以前的filter()返回列表那样正常工作。

全局函数map()#

filter()作的改变一样,map()函数现在返回一个迭代器。(在Python 2里,它返回一个列表。)

NotesPython 2Python 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
  1. 类似对filter()的处理,在最简单的情况下,2to3会用一个list()函数来包装map()调用。

  2. 对于特殊的map(None, ...)语法,跟filter(None, ...)类似,2to3会将其转换成一个使用list()的等价调用

  3. 如果map()的第一个参数是一个lambda函数,2to3会将其等价地转换成列表解析。

  4. 对于会遍历整个序列的for循环,不需要做改变。

  5. 再一次地,这里不需要做修改,因为列表解析会遍历整个序列,即使map()的返回值是迭代器而不是列表它也能正常工作。

全局函数reduce()#

在Python 3里,reduce()函数已经被从全局名字空间里移除了,它现在被放置在fucntools模块里。

NotesPython 2Python 3
 reduce(a, b, c)
from functools import reduce
reduce(a, b, c)

全局函数apply()#

Python 2有一个叫做apply()的全局函数,它使用一个函数f和一个列表[a, b, c]作为参数,返回值是f(a, b, c)。你也可以通过直接调用这个函数,在列表前添加一个星号(*)作为参数传递给它来完成同样的事情。在Python 3里,apply()函数不再存在了;必须使用星号标记法。

NotesPython 2Python 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)
  1. 最简单的形式,可以通过在参数列表(就像[a, b, c]一样)前添加一个星号来调用函数。这跟Python 2里的apply()函数是等价的。

  2. 在Python 2里,apply()函数实际上可以带3个参数:一个函数,一个参数列表,一个字典命名参数(dictionary of named arguments)。在Python 3里,你可以通过在参数列表前添加一个星号(*),在字典命名参数前添加两个星号(**)来达到同样的效果。

  3. 运算符+在这里用作连接列表的功能,它的优先级高于运算符*,所以没有必要在a_list_of_args + z周围添加额外的括号。

  4. 2to3脚本足够智能来转换复杂的apply()调用,包括调用导入模块里的函数。

全局函数intern()#

在Python 2里,你可以用intern()函数作用在一个字符串上来限定(intern)它以达到性能优化。在Python 3里,intern()函数被转移到sys模块里了。

NotesPython 2Python 3
 intern(aString)sys.intern(aString)

exec语句#

就像print语句在Python 3里变成了一个函数一样,exec语句也是这样的。exec()函数使用一个包含任意Python代码的字符串作为参数,然后就像执行语句或者表达式一样执行它。exec()eval()是相似的,但是exec()更加强大并更具有技巧性。eval()函数只能执行单独一条表达式,但是exec()能够执行多条语句,导入(import),函数声明 — 实际上整个Python程序的字符串表示也可以。

NotesPython 2Python 3
exec codeStringexec(codeString)
exec codeString in a_global_namespaceexec(codeString, a_global_namespace)
exec codeString in a_global_namespace,a_local_namespaceexec(codeString, a_global_namespace,a_local_namespace)
  1. 在最简单的形式下,因为exec()现在是一个函数,而不是语句,2to3会把这个字符串形式的代码用括号围起来。

  2. Python 2里的exec语句可以指定名字空间,代码将在这个由全局对象组成的私有空间里执行。Python 3也有这样的功能;你只需要把这个名字空间作为第二个参数传递给exec()函数。

  3. 更加神奇的是,Python 2里的exec语句还可以指定一个本地名字空间(比如一个函数里声明的变量)。在Python 3里,exec()函数也有这样的功能。

execfile语句#

就像以前的exec语句,Python 2里的execfile语句也可以像执行Python代码那样使用字符串。不同的是exec使用字符串,而execfile则使用文件。在Python 3里,execfile语句已经被去掉了。如果你真的想要执行一个文件里的Python代码(但是你不想导入它),你可以通过打开这个文件,读取它的内容,然后调用compile()全局函数强制Python解释器编译代码,然后调用新的exec()函数。

NotesPython 2Python 3
 execfile('a_filename')exec(compile(open('a_filename').read(),'a_filename','exec'))

repr(反引号)#

在Python 2里,为了得到一个任意对象的字符串表示,有一种把对象包装在反引号里(比如`x`)的特殊语法。在Python 3里,这种能力仍然存在,但是你不能再使用反引号获得这种字符串表示了。你需要使用全局函数repr()

NotesPython 2Python 3
`x`repr(x)
`'PapayaWhip' + `2``repr('PapayaWhip'+ repr(2))
  1. 记住,x可以是任何东西 — 一个类,函数,模块,基本数据类型,等等。repr()函数可以使用任何类型的参数。

  2. 在Python 2里,反引号可以嵌套,导致了这种令人费解的(但是有效的)表达式。2to3足够智能以将这种嵌套调用转换到repr()函数。

try...except语句#

从Python 2到Python 3,捕获异常的语法有些许变化。

NotesPython 2Python 3
try:    import mymoduleexcept ImportError, e
    pass
try:    import mymoduleexcept ImportError as e:    pass
try:    import mymoduleexcept (RuntimeError, ImportError), e
    pass
try:    import mymoduleexcept (RuntimeError, ImportError) as e:    pass
try:    import mymoduleexcept ImportError:    pass
no change
try:    import mymoduleexcept:    pass
no change
  1. 相对于Python 2里在异常类型后添加逗号,Python 3使用了一个新的关键字,as

  2. 关键字as也可以用在一次捕获多种类型异常的情况下。

  3. 如果你捕获到一个异常,但是并不在意访问异常对象本身,Python 2和Python 3的语法是一样的。

  4. 类似地,如果你使用一个保险方法(fallback)来捕获所有异常,Python 2和Python 3的语法是一样的。

☞在导入模块(或者其他大多数情况)的时候,你绝对不应该使用这种方法(指以上的fallback)。不然的话,程序可能会捕获到像KeyboardInterrupt(如果用户按Ctrl-C来中断程序)这样的异常,从而使调试变得更加困难。

raise语句#

Python 3里,抛出自定义异常的语法有细微的变化。

NotesPython 2Python 3
raise MyExceptionunchanged
raise MyException,'error message'raise MyException('error message')
raise MyException,'error message',a_tracebackraise MyException('error message').with_traceback(a_traceback)
raise 'error message'unsupported
  1. 抛出不带用户自定义错误信息的异常,这种最简单的形式下,语法没有改变。

  2. 当你想要抛出一个带用户自定义错误信息的异常时,改变就显而易见了。Python 2用一个逗号来分隔异常类和错误信息;Python 3把错误信息作为参数传递给异常类。

  3. Python 2支持一种更加复杂的语法来抛出一个带用户自定义回溯(stack trace,堆栈追踪)的异常。在Python 3里你也可以这样做,但是语法完全不同。

  4. 在Python 2里,你可以抛出一个不带异常类的异常,仅仅只有一个异常信息。在Python 3里,这种形式不再被支持。2to3将会警告你它不能自动修复这种语法。

转载于:https://my.oschina.net/aichirou/blog/671535

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值