python2与python3语法区别
概述
原稿地址:
⼏乎所有的Python 2程序都需要⼀些修改才能正常地运⾏在Python 3的环境下。为了简化这个转换过程,Python 3⾃带了⼀个叫做2to3的实⽤脚本(Utility Script),这个脚本会将你的Python 2程序源⽂件作为输⼊,然后⾃动将其转换到Python 3的形式。描述了如何运⾏这个脚本,然后展⽰了⼀些它不能⾃动修复的情况。这篇附录描述了它能够⾃动修复的内容。
print语句
在Python 2⾥,print是⼀个语句。⽆论你想输出什么,只要将它们放在关键字后边就可以。在Python 3⾥,print()是⼀个函数。就像其他的函数⼀样,print()需要你将想要输出的东西作为参数传给它。
Notes Python 2Python 3
①print print()
②print 1print(1)
③print 1,2print(1,2)
④print 1,2,print(1,2, end=' ')
⑤print >>sys.stderr,1, 2, 3print(1,2, 3, file=sys.stderr)
1. 为输出⼀个空⽩⾏,需要调⽤不带参数的print()。
2. 为输出⼀个单独的值,需要将这这个值作为print()的⼀个参数就可以了。
3. 为输出使⽤⼀个空格分隔的两个值,⽤两个参数调⽤print()即可。
4. 这个例⼦有⼀些技巧。在Python 2⾥,如果你使⽤⼀个逗号(,)作为print语句的结尾,它将会⽤空格分隔输出的结果,然后在输出⼀个尾
随的空格(trailing space),⽽不输出回车(carriage return)。在Python 3⾥,通过把end=' '作为⼀个关键字参数传给print()可以实现同样的效果。参数end的默认值为'\n',所以通过重新指定end参数的值,可以取消在末尾输出回车符。
5. 在Python 2⾥,你可以通过使⽤>>pipe_name语法,把输出重定向到⼀个管道,⽐如sys.stderr。在Python 3⾥,你可以通过将管道作为关
unicode字符的种类有
键字参数file的值传递给print()来完成同样的功能。参数file的默认值为std.stdout,所以重新指定它的值将会使print()输出到⼀个另外⼀个管道。
Unicode字符串
Python 2有两种字符串类型:Unicode字符串和⾮Unicode字符串。Python 3只有⼀种类型:。
Notes Python 2Python 3
①u'PapayaWhip''PapayaWhip'
②ur'PapayaWhip\foo'r'PapayaWhip\foo'
1. Python 2⾥的Unicode字符串在Python 3⾥即普通字符串,因为在Python 3⾥字符串总是Unicode形式的。
2. Unicode原始字符串(raw string)(使⽤这种字符串,Python不会⾃动转义反斜线"\")也被替换为普通的字符串,因为在Python 3⾥,所有
原始字符串都是以Unicode编码的。
全局函数unicode()
Python 2有两个全局函数可以把对象强制转换成字符串:unicode()把对象转换成Unicode字符串,还有str()把对象转换为⾮Unicode字符串。Python 3只有⼀种字符串类型,,所以str()函数即可完成所有的功能。(unicode()函数在Python 3⾥不再存在了。)
Notes Python 2Python 3
unicode(anything)str(anything)
long长整型
Python 2有为⾮浮点数准备的int和long类型。int类型的最⼤值不能超过,⽽且这个最⼤值是平台相关的。可以通过在数字的末尾附上⼀个L来定义长整型,显然,它⽐int类型表⽰的数字范围更⼤。在Python 3⾥,,⼤多数情况下,它很像Python 2⾥的长整型。由于已经不存在两种类型的整数,所以就没有必要使⽤特殊的语法去区别他们。
Notes Python 2Python 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)
1. 在Python 2⾥的⼗进制长整型在Python 3⾥被替换为⼗进制的普通整数。
2. 在Python 2⾥的⼗六进制长整型在Python 3⾥被替换为⼗六进制的普通整数。
3. 在Python 3⾥,由于长整型已经不存在了,⾃然原来的long()函数也没有了。为了强制转换⼀个变量到整型,可以使⽤int()函数。
4. 检查⼀个变量是否是整型,获得它的数据类型,并与⼀个int类型(不是long)的作⽐较。
5. 你也可以使⽤isinstance()函数来检查数据类型;再强调⼀次,使⽤int,⽽不是long,来检查整数类型。
<> ⽐较运算符
Python 2⽀持<>作为!=的同义词。Python 3只⽀持!=,不再⽀持<>了。
Notes Python 2Python 3
①if x <> y:if x != y:
②if x <> y<> z:if x != y!= z:
1. 简单地⽐较。
2. 相对复杂的三个值之间的⽐较。
字典类⽅法has_key()
在Python 2⾥,字典对象的has_key()⽅法⽤来测试字典是否包含特定的键(key)。Python 3不再⽀持这个⽅法了。你需要使⽤。
Notes Python 2Python 3
①a_dictionary.has_key('PapayaWhip')'PapayaWhip' in a_dictionary
②a_dictionary.has_key(x)or a_dictionary.has_key(y)x in a_dictionaryor y in a_dictionary
③a_dictionary.has_key(xor 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)
1. 最简单的形式。
2. 运算符or的优先级⾼于运算符in,所以这⾥不需要添加括号。
3. 另⼀⽅⾯,出于同样的原因 — or的优先级⼤于in,这⾥需要添加括号。(注意:这⾥的代码与前⾯那⾏完全不同。Python会先解释x or
y,得到结果x(如果x)或者y。然后Python检查这个结果是不是a_dictionary的⼀个键。)
4. 运算符in的优先级⼤于运算符+,所以代码⾥的这种形式从技术上说不需要括号,但是2to3还是添加了。
5. 这种形式⼀定需要括号,因为in的优先级⼤于+。
返回列表的字典类⽅法
在Python 2⾥,许多字典类⽅法的返回值是列表。其中最常⽤⽅法的有keys,items和values。在Python 3⾥,所有以上⽅法的返回值改为动态视图(dynamic view)。在⼀些上下⽂环境⾥,这种改变并不会产⽣影响。如果这些⽅法的返回值被⽴即传递给另外⼀个函数,并且那个函数会遍历整个序列,那么以上⽅法的返回值是列表或者视图并不会产⽣什么不同。在另外⼀些情况下,Python 3的这些改变⼲系重⼤。如果你期待⼀个能被独⽴寻址元素的列表,那么Python 3的这些改变将会使你的代码卡住(choke),因为视图(view)不⽀持索引(indexing)。Notes Python 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()⽅法的)到列表的转换。2to3对values()⽅法返回值的转换也是⼀样的。
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。
Notes Python 2Python 3
①import httplib import http.client
②import Cookie kies
③import cookielib kiejar
④import BaseHTTPServer
import SimpleHTTPServer
import CGIHttpServer
import http.server
1. http.client模块实现了⼀个底层的库,可以⽤来请求HTTP资源,解析HTTP响应。
2. kies模块提供⼀个蟒样的(Pythonic)接⼝来获取通过HTTP头部(HTTP header)Set-Cookie发送的cookies
3. 常⽤的流⾏的浏览器会把cookies以⽂件形式存放在磁盘上,kiejar模块可以操作这些⽂件。
4. http.server模块实现了⼀个基本的HTTP服务器
urllib
Python 2有⼀些⽤来分析,编码和获取URL的模块,但是这些模块就像⽼⿏窝⼀样相互重叠。在Python 3⾥,这些模块被重构、组合成了⼀个单独的包,即urllib。
Notes Python 2Python 3
①import urllib quest, urllib.parse,
②import quest,
③import urlparse import urllib.parse
④import robotparser botparser
⑤from urllib import FancyURLopener
from urllib import urlencode quest import FancyURLopener from urllib.parse import urlencode
⑥from urllib2 import Request
from urllib2 import HTTPError quest import Request 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. botparse模块解析。
5. 处理HTTP重定向和其他状态码的FancyURLopener类在Python 3⾥的quest模块⾥依然有效。urlencode()函数已经被转移到
了urllib.parse⾥。
6. Request对象在quest⾥依然有效,但是像HTTPError这样的常量已经被转移到了⾥。
我是否有提到2to3也会重写你的函数调⽤?⽐如,如果你的Python 2代码⾥导⼊了urllib模块,调⽤了urllib.urlopen()函数获取数据,2to3会同时修改import语句和函数调⽤。
Notes Python 2Python 3
import urllib
print urllib.urlopen('/').read()quest, urllib.parse,
quest.urlopen('/').read())
dbm
所有的DBM克隆(DBM clone)现在在单独的⼀个包⾥,即dbm。如果你需要其中某个特定的变体,⽐如GNUDBM,你可以导⼊dbm包中合适的模块。
Notes Python 2Python 3
import dbm import dbm.ndbm
import gdbm u
import dbhash import dbm.bsd
import dumbdbm import dbm.dumb
import anydbm
import whichdb
import dbm
xmlrpc
XML-RPC是⼀个通过HTTP协议执⾏远程RPC调⽤的轻重级⽅法。⼀些XML-RPC客户端和XML-RPC服务端的实现库现在被组合到了独⽴的包,即xmlrpc。
Notes Python 2Python 3 import xmlrpclib import xmlrpc.client
import DocXMLRPCServer
import SimpleXMLRPCServer
import xmlrpc.server 其他模块
Notes Python 2Python 3
①try:
import cStringIO as StringIO except ImportError:
import StringIO
import io
②try:
import cPickle as pickle
except ImportError:
import pickle
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
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的⼀个类。你会怎样做?
Notes Python 2Python 3
①import constants from .import constants
②from mbcharsetproberimport MultiByteCharSetProber from .mbcharsetproberimport MultiByteCharsetProber
1. 当你需要从包的其他地⽅导⼊整个模块,使⽤新的from . import语法。这⾥的句号(.)即表⽰当前⽂件(universaldetector.py)和你想要导⼊⽂件
(constants.py)之间的相对路径。在这个样例中,这两个⽂件在同⼀个⽬录⾥,所以使⽤了单个句号。你也可以从⽗⽬录(from .. import anothermodule)或者⼦⽬录⾥导⼊。
2. 为了将⼀个特定的类或者函数从其他模块⾥直接导⼊到你的模块的名字空间⾥,在需要导⼊的模块名前加上相对路径,并且去掉最后
⼀个斜线(slash)。在这个例⼦中,mbcharsetprober.py与universaldetector.py在同⼀个⽬录⾥,所以相对路径名就是⼀个句号。你也可以从⽗⽬录(from .. import anothermodule)或者⼦⽬录⾥导⼊。
迭代器⽅法next()
在Python 2⾥,迭代器有⼀个next()⽅法,⽤来返回序列⾥的下⼀项。在Python 3⾥这同样成⽴,但是现在有了⼀个新的全局的函数,它使⽤⼀个迭代器作为参数。
Notes Python 2Python 3
①()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 = 42
for an_iterator in a_sequence_of_iterators:
()
next = 42
for an_iterator in a_sequence_of_iterators:
an_iterator.__next__()
1. 最简单的例⼦,你不再调⽤⼀个迭代器的next()⽅法,现在你将迭代器⾃⾝作为参数传递给全局函数next()。
2. 假如你有⼀个返回值是迭代器的函数,调⽤这个函数然后把结果作为参数传递给next()函数。(2to3脚本⾜够智能以正确执⾏这种转换。)
3. 假如你想定义你⾃⼰的类,然后把它⽤作⼀个迭代器,在Python 3⾥,你可以通过定义特殊⽅法__ne
xt__()来实现。
4. 如果你定义的类⾥刚好有⼀个next(),它使⽤⼀个或者多个参数,2to3执⾏的时候不会动它。这个类不能被当作迭代器使⽤,因为它
的next()⽅法带有参数。
5. 这⼀个有些复杂。如果你恰好有⼀个叫做next的本地变量,在Python 3⾥它的优先级会⾼于全局函数next()。在这种情况下,你需要调
⽤迭代器的特别⽅法__next__()来获取序列⾥的下⼀个元素。(或者,你也可以重构代码以使这个本地变量的名字不叫next,但是2to3不会为你做这件事。)
全局函数filter()
在Python 2⾥,filter()⽅法返回⼀个列表,这个列表是通过⼀个返回值为True或者False的函数来检测序列⾥的每⼀项得到的。在Python 3
⾥,filter()函数返回⼀个迭代器,不再是列表。
Notes Python 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()
跟作的改变⼀样,map()函数现在返回⼀个迭代器。(在Python 2⾥,它返回⼀个列表。)
Notes Python 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模块⾥。