生成器的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 iin xrange(10)] [i for iin 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)是一个代码对象,表示编译后的函数体。 I/O方法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: def nonzero(self): pass class A: def bool(self): pass ② class A: def nonzero(self, x, y): pass 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 importmaxint from sys importmaxsize ② 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 iin 1,2] [i for iin (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): pass unchanged ② class Whip: metaclass = PapayaMeta class Whip(metaclass=PapayaMeta): pass ③ class C(Whipper, Beater): metaclass = PapayaMeta class C(Whipper, Beater, metaclass=PapayaMeta): pass 在声明类的时候声明metaclass参数,这在Python 2和Python 3里都有效,它们是一样的。 在类的定义里声明__metaclass__属性在Python 2里有效,但是在Python 3里不再有效。 2to3能够构建一个有效的类声明,即使这个类继承自多个父类。