Python 3.0 与Python 2.x的新意之处 | Yet Another Thoughts
Python 3.0于2008年公布,而2.x版本的最新版本2.7是在2010年中发布的,声明是2.x版本最后的扩展更新。在3.x版本进入一个稳定、活跃的开发阶段并且稳定版更新超过5年后包括2014年的3.4版本后,2.x分支将不会再更新。意味着所有最新的标准库更新默认将只会更新3.x分支的版本。
3.x版本的主要改进包括对Unicode的更好的支持,语言核心几个方面的改变会给新学者更容易学习,整个语言更加的一致,一些旧的弊端被移除了。
下面根据Python创始人Guido van Rossum的文章What’s New In Python 3.0,说明Python 2.6与Python 3.0的主要区别。
1. print不再是关键字,而是一个print()函数
#Old: print "The answer is", 2*2#New: print("The answer is", 2*2)#Old: print x, # Trailing comma suppresses newline#New: print(x, end=" ") # Appends a space instead of a newline#Old: print # Prints a newline#New: print() # You must call the function!#Old: print >>sys.stderr, "fatal error"#New: print("fatal error", file=sys.stderr)#Old: print (x, y) # prints repr((x, y))#New: print((x, y)) # Not the same as print(x, y)!你可以设定字符串间的分隔符:
print("There are ", 2**32, " possibilities!", sep="-")#output: There are -4294967296- possibilities!2. 用view或者iterator替代了List
比如:
- dict的方法dict.keys(), dict.items(), ditct.values()不再返回lists而是返回views。views提供了一个对dict里的entries的一个动态的视角,当其中的数据变化时,views里的数据也随着变化。dict的views可以被迭代以yield出对应的数据。其对应三种使用方法:len(dictview), iter(dictview), x in dictview。keys views与items views都是是unique的同时是hashable的,其行为类似于set,其抽象的基类collections.abc.Set所具有的行为(==, < , ^,等)都是可用的。
- 同时,dict.iterkeys(), dict.iteritems(), dict.itervalues()都不再支持。
- map(), filter(), functools.reduce()函数返回的是iterators而不是lists。你可以用list(map(…))这样包装得到对应的list结果。最好是使用list comprehension或者重构成不需要list的代码。对于传入多个不等长的列表,2.x中的处理方法是视缺失的元素为None补齐,而3.x中是按最短的参数返回结果。如若在3.x版本中得到2.x中应当得到的结果:
python2.x代码:
map(func, *sequences)python3.x代码:
list(map(func, itertools.zip_longest(*sequences)))- 在3.x中,range其实是一个immutable sequence type,type(range(5))得到的类型是。而在2.x版本中,返回的是一个list。
- zip()返回的是一个iterator。而在2.x中,返回的是一个list。
3. 排序比较函数
Python 3.x简化了排序的比较规则:
- 当操作数间没有一个有意义的自然的排序顺序的话,用于排序的操作符会抛出一个TypeError:unroderable types。比如这样的表达式:1 < ‘’, 0 > None, len <= len都不再是有效的表达式。这样的后果是对一个异构的list进行排序变得没有意义,因为他们一些元素之间可能无法比较。这并不会影响==与!=操作符,不同的无法比较的类型的对象永远是不等的。
- builtin.sorted()与list.sort()不再接受cmp参数作为比较函数。全部使用key参数。
- cmp()函数可以视作无用了。cmp()特殊方法也不再支持。使用lt()用来排序,在需要的时候用eq()与hash()与其他复杂的比较。
4. 整型
- long重命名为int。意味着,只有一种内置整型类型。但用起来像是原来的long。
- 像1/2这样的表达式返回一个float而不再是截断的整型。使用1//2得到截断的结果(这个语法从2.2就开始支持了)。
- sys.maxint常量已去除,整型并无值上的限制。sys.maxsize作为一个大于任何实际上的list或者string索引位置的整型。
- repr()对输入一个长整型并不会再跟一个L在尾部。
- 八进制数的形式不再是07200,而是0o720。
5. Text vs. Data 而不是 Unicode vs. 8-bit
- Python 3.x使用了text和(二进制)data的概念。而不是2.x时代的Unicode字符串与8-bit字符串。所有的text都是unicode;编码后的unicode表示成二进制data。类型str用于表示text,bytes用于表示data。与2.x不同的是,3.x中,任何混合使用都会触发TypeError。在2.x中,如果unicode的字符正好包含的都是7字节的ASCII字符,便可以混用;否则你会收到一个UnicodeDecodeError。这样的设计造成了许多隐藏的bugs。
- 在Python 2.x中,Unicode字符串要这样用:u”…”。而在Python 3.x中并不需要。但对二进制字面量数据要使用b”…”。
- str.encode()与bytes.decode()来在二者间互相转换。bytes(s, encoding=…),str(b, encoding=…)也可以做转换。
- str与bytes都是immutable的。bytearray是bytes的mutable实现。
- 在raw字符串中所有的\都按照字符理解。在py3中,r’\u20ac’是6个字符,在py2.x中是单字符。
- 在py3中,内置的basestring抽象类型被移除了。str与bytes之间并没有很多可以共享的功能。
- 文件默认以text文件模式打开,需要在mode参数中加’b’来使用二进制data方式打开。py会使用编码方式来对disk上的bytes和内存中的unicode进行映射。在py3中已经不需要使用codecs模块中的encoding-aware流了。sys.stdin,sys.stdout,sys.stderr现在都是unicode的text类型。若要读取二进制字节数据,须使用io.TextIOBase.buffer属性。
- 默认源代码的编码是UTF-8。
- 不再有StringIO和cStringIO这两个模块。取而代之,import io模块,使用io.StringIO或者io.BytesIO处理text或者data。
Unicode HOWTO有py3的Unicode详细介绍。
这篇博文比较详细地介绍了各个常用字符编码还有Python字符编码的相关问题。
6. 语法更新(selected)
新语法
- 函数的参数与返回值的注释。这些注释并不包含语义。
def func0(s: str, b: 'a string', c: Plane) -> Tank: pass- 关键字参数可以跟在类声明的基类列表之后。比如新的语法中设定一个类的metaclass:
class RealThing(Thing, metaclass=RealThingMeta): pass而不是:
class RealThing(Thing): __metaclass__ = RealThingMeta- 扩展的iterable的拆包。可以这样写:
(a, b, *rest, c) = range(15) - 0o720表示八进制,0b1010表示二进制。b’…’表示bytes。
修改的语法
- 新的raise语法:
raise [expr [from expr]]。 raise后的第一个表达式是一个BaseException的之类或者一个实例。当是个类型时异常的实例会在需要的时候使用无参的构造器创建出来。from用于构造异常链。如果给出,第二个表达式也是一个异常的类型或者实例,它会被绑定到前一个异常上作为__cause__参数。如果这个异常没有被处理,两个异常都会被报告出来。 with与as成为了保留词。with … as …用于将一个块的代码的执行用一个contaxt manager定义的__enter__()与__exit__()方法包裹起来。with语句保证如果__enter__()方法没有错误地调用完毕,那么__exit__()方法肯定会被调用。__enter__()的返回值赋值给as后的target,代码块中抛出异常的类型、值、异常链会作为参数传给__exit__()方法,如果__exit__()方法的返回值是False,异常会被再次抛出;返回值是True时,异常被压下,继续执行代码。以下是示例代码:
class controlled_execution:
def __enter__(self): set things up return thing def __exit__(self, type, value, traceback): tear things down
with controlled_execution() as thing: some codeUnderstanding Python’s “with” statement中介绍了py3的with用法,推荐一下。
- List comprehensions不再支持语法[… for var in item1, item2, …],而是[… for var in (item1, item2, …)]。
String格式化新语法,使用str.format()。一个例子:
"...{kw0}...{kw1}...".format(kw0='...',kw1='...')而不是"...%(kw0)s...%(kw1)s..." % {'kw0': '...', 'kw1': '...'}删除的语法
Tuple参数拆包被移除。
def foo(a, (b, c)):...不能使用,而是def foo(a, b_c): b, c = b_c。- <>不再使用,只能用!=。
- exec不再是关键字,而是一个函数。
- 整型字面量不再跟一个l或者L。
- 字符串字面量不再前缀一个u或者U。
- from import 语法只能在module中使用,不能在方法内部使用。
- 相对的import的语法只能是: from .[module] import name。任何非以.开头的import都被认为是绝对路径import。
全部是新式类,不再有旧式类。不用这样:
class A(object):pass而是写class A: ...就好。旧式类是classtype类型,而新式类是type类型。7. 库更新
比较繁杂,请参考原文。
在2.x版本中,通常的pattern是:相同功能有一个纯python实现的模块,有一个可选的使用C扩展的加速版本,比如pickle和cPickle。在Py3中,加速版本被认为是纯python版本的实现细节,开发者应当永远import标准的模块。pickle/cPickle便是这种待遇,在3.1之后profile也是这样的。而StringIO模块已成为了io模块中的一个类。
8.异常相关的修改
- 所有的异常都必须是BaseException的之类。原来这个是推荐做法,现在有了限制。
- 基本上所有的异常都由Exception衍生,BaseException应当只有最top level的事情才会使用。
- 使用
raise Exception(args)代替raise Exception, args. - 使用
except SomeException as variable代替except SomeException, variable。对应变量会随着except快退出而被删除。
9. 内置变化
- super()现在可以不用参数便可在类定义里的实例方法中调用。py3会选择正确的类和实例作为参数。比如:
class A: def f(self): print('A.f()')class B(A): def f(self): super().f() #output: A.f() print('B.f()') #output: B.f()- raw_input()被重命名为input()。
- reduce()被从全局空间移除,移到了functools模块下。需使用functools.reduce()。
- dict.has_key()方法被移除。需要使用操作符in。
- apply()被移除。
最后推荐一个Github用户@asmeurer的ppt:10 awesome features of Python that you can’t use because you refuse to upgrade to Python 3