这个星期开始学习Python了,因为看的书都是基于Python2.x,而且我安装的是Python3.1,所以书上写的地方好多都不适用于Python3.1,特意在Google上search了一下3.x和2.x的区别。特此在自己的空间中记录一下,以备以后查找方便,也可以分享给想学习Python的friends.
1.性能
Py3.0运行 pystone benchmark的速度比Py2.5慢30%。Guido认为Py3.0有极大的优化空间,在字符串和整形操作上可
以取得很好的优化结果。
Py3.1性能比Py2.5慢15%,还有很大的提升空间。
2.编码
Py3.X源码文件默认使用utf-8编码,这就使得以下代码是合法的:
>>> 中国 = 'china'
>>>print(中国)
china
3. 语法
1)去除了<>,全部改用!=
2)去除``,全部改用repr()
3)关键词加入as 和with,还有True,False,None
4)整型除法返回浮点数,要得到整型结果,请使用//
5)加入nonlocal语句。使用noclocal x可以直接指派外围(非全局)变量
6)去除print语句,加入print()函数实现相同的功能。同样的还有 exec语句,已经改为exec()函数
例如:
2.X: print "The answer is", 2*2
3.X: print("The answer is", 2*2)
2.X: print x, # 使用逗号结尾禁止换行
3.X: print(x, end=" ") # 使用空格代替换行
2.X: print # 输出新行
3.X: print() # 输出新行
2.X: print >>sys.stderr, "fatal error"
3.X: print("fatal error", file=sys.stderr)
2.X: print (x, y) # 输出repr((x, y))
3.X: print((x, y)) # 不同于print(x, y)!
7)改变了顺序操作符的行为,例如x
2.X:guess = int(raw_input('Enter an integer : ')) # 读取键盘输入的方法
3.X:guess = int(input('Enter an integer : '))
9)去除元组参数解包。不能def(a, (b, c)):pass这样定义函数了
10)新式的8进制字变量,相应地修改了oct()函数。
2.X的方式如下:
>>> 0666
438
>>> oct(438)
'0666'
3.X这样:
>>> 0666
SyntaxError: invalid token (
>>> 0o666
438
>>> oct(438)
'0o666'
11)增加了 2进制字面量和bin()函数
>>> bin(438)
'0b110110110'
>>> _438 = '0b110110110'
>>> _438
'0b110110110'
12)扩展的可迭代解包。在Py3.X 里,a, b, *rest = seq和 *rest, a = seq都是合法的,只要求两点:rest是list
对象和seq是可迭代的。
13)新的super(),可以不再给super()传参数,
>>> class C(object):
def __init__(self, a):
print('C', a)
>>> class D(C):
def __init(self, a):
super().__init__(a) # 无参数调用super()
>>> D(8)
C 8
<__main__.D object at 0x00D7ED90>
14)新的metaclass语法:
class Foo(*bases, **kwds):
pass
15)支持class decorator。用法与函数decorator一样:
>>> def foo(cls_a):
def print_func(self):
print('Hello, world!')
cls_a.print = print_func
return cls_a
>>> @foo
class C(object):
pass
>>> C().print()
Hello, world!
class decorator可以用来玩玩狸猫换太子的大把戏。更多请参阅PEP 3129
4. 字符串和字节串
1)现在字符串只有str一种类型,但它跟2.x版本的unicode几乎一样。
2)关于字节串,请参阅“数据类型”的第2条目
5.数据类型
1)Py3.X去除了long类型,现在只有一种整型——int,但它的行为就像2.X版本的long
2)新增了bytes类型,对应于2.X版本的八位串,定义一个bytes字面量的方法如下:
>>> b = b'china'
>>> type(b)
str对象和bytes对象可以使用.encode() (str -> bytes) or .decode() (bytes -> str)方法相互转化。
>>> s = b.decode()
>>> s
'china'
>>> b1 = s.encode()
>>> b1
b'china'
3)dict的.keys()、.items 和.values()方法返回迭代器,而之前的iterkeys()等函数都被废弃。同时去掉的还有
dict.has_key(),用 in替代它吧
6.面向对象
1)引入抽象基类(Abstraact Base Classes,ABCs)。
2)容器类和迭代器类被ABCs化,所以cellections模块里的类型比Py2.5多了很多。
>>> import collections
>>> print('\n'.join(dir(collections)))
Callable
Container
Hashable
ItemsView
Iterable
Iterator
KeysView
Mapping
MappingView
MutableMapping
MutableSequence
MutableSet
NamedTuple
Sequence
Set
Sized
ValuesView
__all__
__builtins__
__doc__
__file__
__name__
_abcoll
_itemgetter
_sys
defaultdict
deque
另外,数值类型也被ABCs化。关于这两点,请参阅 PEP 3119和PEP 3141。
3)迭代器的next()方法改名为__next__(),并增加内置函数next(),用以调用迭代器的__next__()方法
4)增加了@abstractmethod和 @abstractproperty两个 decorator,编写抽象方法(属性)更加方便。
7.异常
1)所以异常都从 BaseException继承,并删除了StardardError
2)去除了异常类的序列行为和.message属性
3)用 raise Exception(args)代替 raise Exception, args语法
4)捕获异常的语法改变,引入了as关键字来标识异常实例,在Py2.5中:
>>> try:
... raise NotImplementedError('Error')
... except NotImplementedError, error:
... print error.message
...
Error
在Py3.0中:
>>> try:
raise NotImplementedError('Error')
except NotImplementedError as error: #注意这个 as
print(str(error))
Error
5)异常链,因为__context__在3.0a1版本中没有实现
8.模块变动
1)移除了cPickle模块,可以使用pickle模块代替。最终我们将会有一个透明高效的模块。
2)移除了imageop模块
3)移除了 audiodev, Bastion, bsddb185, exceptions, linuxaudiodev, md5, MimeWriter, mimify, popen2,
rexec, sets, sha, stringold, strop, sunaudiodev, timing和xmllib模块
4)移除了bsddb模块(单独发布,可以从http://www.jcea.es/programacion/pybsddb.htm获取)
5)移除了new模块
6)os.tmpnam()和os.tmpfile()函数被移动到tmpfile模块下
7)tokenize模块现在使用bytes工作。主要的入口点不再是generate_tokens,而是 tokenize.tokenize()
9.其它
1)xrange() 改名为range(),要想使用range()获得一个list,必须显式调用:
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2)bytes对象不能hash,也不支持 b.lower()、b.strip()和b.split()方法,但对于后两者可以使用 b.strip(b’
\n\t\r \f’)和b.split(b’ ‘)来达到相同目的
3)zip()、map()和filter()都返回迭代器。而apply()、 callable()、coerce()、 execfile()、reduce()和reload
()函数都被去除了
现在可以使用hasattr()来替换 callable(). hasattr()的语法如:hasattr(string, '__name__')
4)string.letters和相关的.lowercase和.uppercase被去除,请改用string.ascii_letters 等
5)如果x < y的不能比较,抛出TypeError异常。2.x版本是返回伪随机布尔值的
6)__getslice__系列成员被废弃。a[i:j]根据上下文转换为a.__getitem__(slice(I, j))或 __setitem__和
__delitem__调用
7)file类被废弃,在Py2.5中:
>>> file
在Py3.X中:
>>> file
Traceback (most recent call last):
File "
file
NameError: name 'file' is not defined
===========================
难度等级: ♦♦♦♦♦
❝ Life is pleasant. Death is peaceful. It’s the transition that’s troublesome. ❞
— Isaac Asimov (attributed)
几乎所有的Python 2程序都需要一些修改才能正常地运行在Python 3的环境下。为了简化这个转换过程,Python 3自带了一个叫做2to3的实用脚本(Utility Script),这个脚本会将你的Python 2程序源文件作为输入,然后自动将其转换到Python 3的形式。案例研究:将chardet移植到Python 3(porting chardet to Python 3)描述了如何运行这个脚本,然后展示了一些它不能自动修复的情况。这篇附录描述了它能够自动修复的内容。
在Python 2里,print是一个语句。无论你想输出什么,只要将它们放在print关键字后边就可以。在Python 3里,print()是一个函数。就像其他的函数一样,print()需要你将想要输出的东西作为参数传给它。
Notes | Python 2 | Python 3 |
---|---|---|
① | 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) |
Python 2有两种字符串类型:Unicode字符串和非Unicode字符串。Python 3只有一种类型:Unicode字符串(Unicode strings)。
Notes | Python 2 | Python 3 |
---|---|---|
① | u'PapayaWhip' | 'PapayaWhip' |
② | ur'PapayaWhip\foo' | r'PapayaWhip\foo' |
Python 2有两个全局函数可以把对象强制转换成字符串:unicode()把对象转换成Unicode字符串,还有str()把对象转换为非Unicode字符串。Python 3只有一种字符串类型,Unicode字符串,所以str()函数即可完成所有的功能。(unicode()函数在Python 3里不再存在了。)
Notes | Python 2 | Python 3 |
---|---|---|
unicode(anything) | str(anything) |
Python 2有为非浮点数准备的int和long类型。int类型的最大值不能超过sys.maxint,而且这个最大值是平台相关的。可以通过在数字的末尾附上一个L来定义长整型,显然,它比int类型表示的数字范围更大。在Python 3里,只有一种整数类型int,大多数情况下,它很像Python 2里的长整型。由于已经不存在两种类型的整数,所以就没有必要使用特殊的语法去区别他们。
进一步阅读:pep 237:统一长整型和整型。
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只支持!=,不再支持<>了。
Notes | Python 2 | Python 3 |
---|---|---|
① | if x <> y: | if x != y: |
② | if x <> y <> z: | if x != y != z: |
在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) |
在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 |
从Python 2到Python 3,标准库里的一些模块已经被重命名了。还有一些相互关联的模块也被组合或者重新组织,以使得这种关联更有逻辑性。
在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 |
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 |
⑤ | |
|
⑥ | |
|
我是否有提到2to3也会重写你的函数调用?比如,如果你的Python 2代码里导入了urllib模块,调用了urllib.urlopen()函数获取数据,2to3会同时修改import语句和函数调用。
Notes | Python 2 | Python 3 |
---|---|---|
|
|
所有的dbm克隆(dbm clone)现在在单独的一个包里,即dbm。如果你需要其中某个特定的变体,比如gnu dbm,你可以导入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 |
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的时候,为了实现同一个包内模块的相互引用,你会使用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 |
在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 |
⑤ | |
|
在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 |
跟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 |
在Python 3里,reduce()函数已经被从全局名字空间里移除了,它现在被放置在fucntools模块里。
Notes | Python 2 | Python 3 |
---|---|---|
reduce(a, b, c) | |
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) |
在Python 2里,你可以用intern()函数作用在一个字符串上来限定(intern)它以达到性能优化。在Python 3里,intern()函数被转移到sys模块里了。
Notes | Python 2 | Python 3 |
---|---|---|
intern(aString) | sys.intern(aString) |
就像print语句在Python 3里变成了一个函数一样,exec语句也是这样的。exec()函数使用一个包含任意Python代码的字符串作为参数,然后就像执行语句或者表达式一样执行它。exec()跟eval()是相似的,但是exec()更加强大并更具有技巧性。eval()函数只能执行单独一条表达式,但是exec()能够执行多条语句,导入(import),函数声明 — 实际上整个Python程序的字符串表示也可以。
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语句,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')) |
在Python 2里,为了得到一个任意对象的字符串表示,有一种把对象包装在反引号里(比如`x`)的特殊语法。在Python 3里,这种能力仍然存在,但是你不能再使用反引号获得这种字符串表示了。你需要使用全局函数repr()。
Notes | Python 2 | Python 3 |
---|---|---|
① | `x` | repr(x) |
② | `'PapayaWhip' + `2`` | repr('PapayaWhip' + repr(2)) |
从Python 2到Python 3,捕获异常的语法有些许变化。
Notes | Python 2 | Python 3 |
---|---|---|
① | |
|
② | |
|
③ | |
no change |
④ | |
no change |
☞在导入模块(或者其他大多数情况)的时候,你绝对不应该使用这种方法(指以上的fallback)。不然的话,程序可能会捕获到像KeyboardInterrupt(如果用户按Ctrl-C来中断程序)这样的异常,从而使调试变得更加困难。
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里,生成器有一个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里,有两种方法来获得一定范围内的数字: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 |
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()) |
在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__ |
在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) |
☃
在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 |
在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__ |
在Python 2里,你可以创建自己的类,并使他们能够在布尔上下文(boolean context)中使用。举例来说,你可以实例化这个类,并把这个实例对象用在一个if语句中。为了实现这个目的,你定义一个特别的__nonzero__()方法,它的返回值为True或者False,当实例对象处在布尔上下文中的时候这个方法就会被调用 。在Python 3里,你仍然可以完成同样的功能,但是这个特殊方法的名字变成了__bool__()。
Notes | Python 2 | Python 3 |
---|---|---|
① | |
|
② | |
no change |
在Python 2和Python 3之间,定义八进制(octal)数的语法有轻微的改变。
Notes | Python 2 | Python 3 |
---|---|---|
x = 0755 | x = 0o755 |
由于长整型和整型被整合在一起了,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) |
在Python 2里,你可以使用全局函数callable()来检查一个对象是否可调用(callable,比如函数)。在Python 3里,这个全局函数被取消了。为了检查一个对象是否可调用,可以检查特殊方法__call__()的存在性。
Notes | Python 2 | Python 3 |
---|---|---|
callable(anything) | hasattr(anything, '__call__') |
在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 |
在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模块里各种各样的常量能帮助你决定一个对象的类型。在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()函数检查一个对象是否是一个特定类(class)或者类型(type)的实例。在Python 2里,你可以传递一个由类型(types)构成的元组给isinstance(),如果该对象是元组里的任意一种类型,函数返回True。在Python 3里,你依然可以这样做,但是不推荐使用把一种类型作为参数传递两次。
Notes | Python 2 | Python 3 |
---|---|---|
isinstance(x, (int, float, int)) | isinstance(x, (int, float)) |
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) |
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 |
处理异常的时候,在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)] |
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() |
在Python 2里,你可以通过在类的声明中定义metaclass参数,或者定义一个特殊的类级别的(class-level)__metaclass__属性,来创建元类。在Python 3里,__metaclass__属性已经被取消了。
Notes | Python 2 | Python 3 |
---|---|---|
① | |
unchanged |
② | |
|
③ | |
|
以下所列的“修补”(fixes)实质上并不算真正的修补。意思就是,他们只是代码的风格上的事情,而不涉及到代码的本质。但是Python的开发者们在使得代码风格尽可能一致方面非常有兴趣(have a vested interest)。为此,有一个专门o描述Python代码风格的官方指导手册 — 细致到能使人痛苦 — 都是一些你不太可能关心的在各种各样的细节上的挑剔。鉴于2to3为转换代码提供了一个这么好的条件,脚本的作者们添加了一些可选的特性以使你的代码更具可读性。
在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} |
用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} |
在Python社区里建立起来了许多惯例。有一些比如while 1: loop,它可以追溯到Python 1。(Python直到Python 2.3才有真正意义上的布尔类型,所以开发者以前使用1和0替代。)当代的Python程序员应该锻炼他们的大脑以使用这些惯例的现代版。
☞2to3脚本默认不会为这些惯例做修复。为了开启这个功能,在命令行调用2to3的时候指定-f idioms参数。
Notes | Before | After |
---|---|---|
|
|
|
type(x) == T | isinstance(x, T) | |
type(x) is T | isinstance(x, T) | |
|
|