标签:des style http io ar os 使用 java for
Python 3 是 Guido van Rossum 功能强大的通用编程语言的最新版本。它虽然打破了与 2.x 版本的向后兼容性,但却清理了某些语法方面的问题。本文是这个由两部分组成的系列文章中的第二篇,本文构建在此系列 前一期文章 的基础之上,内容涵盖了 Python 更多的新特性和更高深的一些主题,比如在抽象基类、元类和修饰符等方面的变化。
有关 Python 版本 3—,也即 Python 3000 或 Py3K— 的前一篇文章讨论了 Python 内打破向后兼容性的一些基本变化,比如新的 print()
函数、 bytes
数据类型以及 string
类型的变化。本文是该系列文章的第 2 部分,探究了更为高深的一些主题,比如抽象基类(ABC)、元类、函数注释和修饰符(decorator)、整型数(integer literal)支持、数值类型层次结构以及抛出和捕获异常,其中的大多数特性仍然会打破与版本 2x 产品线的向后兼容性。
在 Python 之前的版本中,对方法的转换必须在方法定义之后进行。对于较长的方法,此要求将定义的重要组成部分与 Python Enhancement Proposal (PEP) 318(有关链接,请参见 参考资料)给出的外部接口定义分离。下面的代码片段演示了这一转换要求:
def myMethod(self): # do something myMethod = transformMethod(myMethod)
为了让此类情景更易于读懂,也为了避免必须多次重用相同的方法名,在 Python 版本 2.4 中引入了方法修饰符。
修饰符 是一些方法,这些方法可以修改其他方法并返回一个方法或另外一个可调用对象。对它们的注释是在修饰符的名称前冠以 “at” 符号(@
)— 类似 Java™ 注释的语法。清单 2 显示了实际应用中的修饰符。
@transformMethod def myMethod(self): # do something
修饰符是一些纯粹的语法糖(syntactic sugar)— 或者(如 Wikipedia 所言)“对计算机语言语法的补充,这些补充并不影响语言的功能,而是会让语言变得更易于被人使用。”修饰符的一种常见用法是注释静态方法。比如,清单 1 和清单 2 相当,但清单 2 更容易被人读懂。
定义修饰符与定义其他方法无异:
def mod(method): method.__name__ = "John" return method @mod def modMe(): pass print(modMe.__name__)
更棒的是 Python 3 现在不仅支持针对方法的修饰符,并且支持针对类的修饰符,所以,取代如下的用法:
class myClass: pass myClass = doSomethingOrNotWithClass(myClass)
我们可以这样使用:
@doSomethingOrNotWithClass class myClass: pass
元类 是这样一些类,这些类的实例也是类。Python 3 保留了内置的、用来创建其他元类或在运行时动态创建类的 metaclass
类型。如下的语法仍旧有效:
>>>aClass = type(‘className‘, (object,), {‘magicMethod‘: lambda cls : print("blah blah")})
上述语法接受的参数包括:作为类名的字符串、被继承对象的元组(可以是一个空的元组)和一个包含可以添加的方法的字典(也可以是空的)。当然,也可以从类型继承并创建您自己的元类:
class meta(type): def __new__(cls, className, baseClasses, dictOfMethods): return type.__new__(cls, className, baseClasses, dictOfMethods)
注意:如果上面两个例子起不到任何作用,我强烈建议您阅读 David Mertz 和 Michele Simionato 合写的有关元类的系列文章。相关链接,请参见 参考资料。
请注意,现在,在类定义中,关键字参数被允许出现在基类列表之后 — 通常来讲,即 class Foo(*bases, **kwds): pass
。使用关键字参数 metaclass
将元类传递给类定义。比如:
>>>class aClass(baseClass1, baseClass2, metaclass = aMetaClass): pass
旧的元类语法是将此元类分配给内置属性 __metaclass__
:
class Test(object): __metaclass__ = type
而且,既然有了新的属性 —__prepare__
— 我们就可以使用此属性为新的类名称空间创建字典。在类主体被处理之前,先会调用它,如清单 3 所示。
def meth(): print("Calling method") class MyMeta(type): @classmethod def __prepare__(cls, name, baseClasses): return {‘meth‘:meth} def __new__(cls, name, baseClasses, classdict): return type.__new__(cls, name, baseClasses, classdict) class Test(metaclass = MyMeta): def __init__(self): pass attr = ‘an attribute‘ t = Test() print(t.attr)
我们从 PEP 3115 节选了一个更为有趣的例子,如清单 4 所示,这个例子创建了一个具有其方法名称列表的元类,而同时又保持了类方法声明的顺序。
# The custom dictionary class member_table(dict): def __init__(self): self.member_names = [] def __setitem__(self, key, value): # if the key is not already defined, add to the # list of keys. if key not in self: self.member_names.append(key) # Call superclass dict.__setitem__(self, key, value) # The metaclass class OrderedClass(type): # The prepare function @classmethod def __prepare__(metacls, name, bases): # No keywords in this case return member_table() # The metaclass invocation def __new__(cls, name, bases, classdict): # Note that we replace the classdict with a regular # dict before passing it to the superclass, so that we # don‘t continue to record member names after the class # has been created. result = type.__new__(cls, name, bases, dict(classdict)) result.member_names = classdict.member_names return result
在元类内所做的这些改变有几个原因。对象的方法一般存储于一个字典,而这个字 典是没有顺序的。不过,在某些情况下,若能保持所声明的类成员的顺序将非常有用。这可以通过让此元类在信息仍旧可用时,较早地涉入类的创建得以实现 — 这很有用,比如在 C 结构的创建中。借助这种新的机制还能在将来实现其他一些有趣的功能,比如在类构建期间将符号插入到所创建的类名称空间的主体以及对符号的前向引用。PEP 3115 提到更改语法还有美学方面的原因,但是对此尚存在无法用客观标准解决的争论(到 PEP 3115 的链接,请参见 参考资料)。
正如我在 Python 3 初探,第 1 部分:Python 3 的新特性 中提到的,ABC 是一些不能被实例化的类。Java 或 C++ 语言的程序员应该对此概念十分熟悉。Python 3 添加了一个新的框架 —abc— 它提供了对 ABC 的支持。
这个 abc 模块具有一个元类(ABCMeta
)和 修饰符(@abstractmethod
和 @abstractproperty
)。如果一个 ABC 具有一个 @abstractmethod
或 @abstractproperty
,它就不能被实例化,但必须在一个子类内被覆盖。比如,如下代码:
>>>from abc import * >>>class C(metaclass = ABCMeta): pass >>>c = C()
这些代码是可以的,但是不能像下面这样编码:
>>>from abc import * >>>class C(metaclass = ABCMeta): ... @abstractmethod ... def absMethod(self): ... pass >>>c = C() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can‘t instantiate abstract class C with abstract methods absMethod
更好的做法是使用如下代码:
>>>class B(C): ... def absMethod(self): ... print("Now a concrete method") >>>b = B() >>>b.absMethod() Now a concrete method
ABCMeta
类覆盖属性 __instancecheck__
和 __subclasscheck__
,借此可以重载内置函数 isinstance()
和 issubclass()
。要向 ABC 添加一个虚拟子类,可以使用 ABCMeta
提供的 register()
方法。如下所示的简单示例:
>>>class TestABC(metaclass=ABCMeta): pass >>>TestABC.register(list) >>>TestABC.__instancecheck__([]) True
它等同于使用 isinstance(list, TestABC)
。您可能已经注意到 Python 3 使用 __instancecheck__
,而非 __issubclass__
,使用 __subclasscheck__
,而非 __issubclass__
,这看起来更为自然。若将参数 isinstance(subclass, superclass)
反转成,比如 superclass.__isinstance__(subclass)
,可能会引起混淆。可见,语法 superclass.__instancecheck__(subclass)
显然更好一点。
在 collections
模块内,可以使用几个 ABC 来测试一个类是否提供了特定的一个接口:
>>>from collections import Iterable >>>issubclass(list, Iterable) True
表 1 给出了这个集合框架的 ABC。
ABC | Inherits |
---|---|
Container |
|
Hashable |
|
Iterable |
|
Iterator |
Iterable |
Sized |
|
Callable |
|
Sequence |
Sized , Iterable , Container |
MutableSequence |
Sequence |
Set |
Sized , Iterable , Container |
MutableSet |
Set |
Mapping |
Sized , Iterable , Container |
MutableMapping |
Mapping |
MappingView |
Sized |
KeysView |
MappingView , Set |
ItemsView |
MappingView , Set |
ValuesView |
MappingView |
Python 3 现支持能代表数值类的 ABC 的类型层次结构。这些 ABC 存在于 numbers
模块内并包括 Number
、 Complex
、Real
、 Rational
和 Integral
。图 1 显示了这个数值层次结构。可以使用它们来实现您自己的数值类型或其他数值 ABC。
新模块 fractions
可实现这个数值 ABC Rational
。此模块提供对有理数算法的支持。若使用 dir(fractions.Fraction)
,就会注意到它具有一些属性,比如 imag
、 real
和 __complex__
。根据数值塔的原理分析,其原因在于 Rationals
继承自 Reals
,而 Reals
继承自 Complex
。
在 Python 3 内,except
语句已经被修改为处理语法不清的问题。之前,在 Python version 2.5 内,try . . . except
结构,比如:
>>>try: ... x = float(‘not a number‘) ... except (ValueError, NameError): ... print "can‘t convert type"
可能会被不正确地写为:
>>> try: ... x = float(‘not a number‘) ... except ValueError, NameError: ... print "can‘t convert type"
后一种格式的问题在于 ValueError
异常将永远捕获不到,因为解释器将会捕获 ValueError
并将此异常对象绑定到名称 NameError
。这一点可以从下面的示例中看出:
>>> try: ... x = float(‘blah‘) ... except ValueError, NameError: ... print "NameError is ", NameError ... NameError is invalid literal for float(): not a number
所以,为了处理语法不清的问题,在想要将此异常对象与另一个名称绑定时,逗号(,
)会被替换成关键字 as
。如果想要捕获多个异常,必须使用括号(()
)。清单 5 中的代码展示了 Python 3 内的两个合乎语法的示例。
# bind ValueError object to local name ex try: x = float(‘blah‘) except ValueError as ex: print("value exception occurred ", ex) # catch two different exceptions simultaneously try: x = float(‘blah‘) except (ValueError, NameError): print("caught both types of exceptions")
异常处理的另一个改变是异常链— 隐式或显式。清单 6 给出了隐式异常链的一个示例。
def divide(a, b): try: print(a/b) except Exception as exc: def log(exc): fid = open(‘logfile.txt‘) # missing ‘w‘ print(exc, file=fid) fid.close() log(exc) divide(1,0)
divide()
方法试图执行除数为零的除法,因而引发了一个异常:ZeroDivisionError
。但是,在异常语句的嵌套 log()
方法内,print(exc, file=fid)
试图向一个尚未打开的文件进行写操作。Python 3 抛出异常,如清单 7 所示。
Traceback (most recent call last): File "chainExceptionExample1.py", line 3, in divide print(a/b) ZeroDivisionError: int division or modulo by zero During handling of the above exception, another exception occurred: Traceback (most recent call last): File "chainExceptionExample1.py", line 12, in <module> divide(1,0) File "chainExceptionExample1.py", line 10, in divide log(exc) File "chainExceptionExample1.py", line 7, in log print(exc, file=fid) File "/opt/python3.0/lib/python3.0/io.py", line 1492, in write self.buffer.write(b) File "/opt/python3.0/lib/python3.0/io.py", line 696, in write self._unsupported("write") File "/opt/python3.0/lib/python3.0/io.py", line 322, in _unsupported (self.__class__.__name__, name)) io.UnsupportedOperation: BufferedReader.write() not supported
请注意,这两个异常都被处理。在 Python 的早期版本中,ZeroDivisionError
将会丢失,得不到处理。那么这是如何实现的呢?__context__
属性,比如 ZeroDivisionError
,现在是所有异常对象的一部分。在本例中,被抛出的 IOError
的 __context__
属性 “仍为” __context__
属性内的 ZeroDivisionError
。
除 __context__
属性外,异常对象还有一个 __cause__
属性,它通常被初始化为 None
。这个属性的作用是以一种显式方法记录异常的原因。__cause__
属性通过如下语法设置:
>>> raise EXCEPTION from CAUSE
它与下列代码相同:
>>>exception = EXCEPTION >>>exception.__cause__ = CAUSE >>>raise exception
但更为优雅。清单 8 中所示的示例展示了显式异常链。
class CustomError(Exception): pass try: fid = open("aFile.txt") # missing ‘w‘ again print("blah blah blah", file=fid) except IOError as exc: raise CustomError(‘something went wrong‘) from exc
正如之前的例子所示,print()
函数抛出了一个异常,原因是文件尚未打开以供写入。清单 9 给出了相应的跟踪。
Traceback (most recent call last): File "chainExceptionExample2.py", line 5, in <module> fid = open("aFile.txt") File "/opt/python3.0/lib/python3.0/io.py", line 278, in __new__ return open(*args, **kwargs) File "/opt/python3.0/lib/python3.0/io.py", line 222, in open closefd) File "/opt/python3.0/lib/python3.0/io.py", line 615, in __init__ _fileio._FileIO.__init__(self, name, mode, closefd) IOError: [Errno 2] No such file or directory: ‘aFile.txt‘ The above exception was the direct cause of the following exception: Traceback (most recent call last): File "chainExceptionExample2.py", line 8, in <modulei> raise CustomError(‘something went wrong‘) from exc __main__.CustomError: something went wrong
请注意,在异常跟踪中的一行 “The above exception was the direct cause of the following exception,” 之后的是另一个对导致 CustomError
“something went wrong” 异常的跟踪。
添加给异常对象的另一个属性是 __traceback__
。如果被捕获的异常不具备其 __traceback__
属性,那么新的跟踪就会被设置。如下是一个简单的例子:
from traceback import format_tb try: 1/0 except Exception as exc: print(format_tb(exc.__traceback__)[0])
请注意,format_tb
返回的是一个列表,而且此列表中只有一个异常。
Python 支持不同进制的整型字符串文本 — 八进制、十进制(最明显的!)和十六进制 — 而现在还加入了二进制。八进制数的表示已经改变:八进制数现在均以一个前缀 0o
或 0O
(即,数字零后跟一个大写或小写的字母 o)开头。比如,八进制的 13 或十进制的 11 分别如下表示:
>>>0o13 11
新的二进制数则以前缀 0b
或 0B
(即,数字零后跟一个大写或小写的字母 b)开头。十进制数 21 用二进制表示应为:
>>>0b010101 21
oct()
和 hex()
方法已被删除。
函数注释 会在编译时将表述与函数的某些部分(比如参数)相关联。就其本身而言,函数注释是无意义的 — 即,除非第三方库对之进行处理,否则它们不会被处理。函数注释的作用是为了标准化函数参数或返回值被注释的方式。函数注释语法为:
def methodName(param1: expression1, ..., paramN: expressionN)->ExpressionForReturnType: ...
例如,如下所示的是针对某函数的参数的注释:
def execute(program:"name of program to be executed", error:"if something goes wrong"): ...
如下的示例则注释了某函数的返回值。这对于检查某函数的返回类型非常有用:
def getName() -> "isString": ...
函数注释的完整语法可以在 PEP 3107(相关链接,请参见 参考资料)内找到。
Python 3 最终的发布版在 2008 年 12 月份初就已经发布。自那之后,我曾经查阅过一些博客,试图了解人们对向后不兼容性问题的反应。虽然,我不能断言社区在某些程度上已经达成了官方的共识,但 我阅读过的博客所呈现的观点呈两极分化。Linux® 开发社区似乎不太喜欢转变到版本 3,因为需要移植大量代码。相比之下,因为新版本对 unicode 支持方面的改进,很多 Web 开发人员则欢迎转变。
我想要说明的一点是,在您做决定是否要移植到新版本之前,应该仔细阅 读相关的 PEP 和开发邮件列表。这些 PEP 解释了各项更改的初衷、带来的益处及其实现。不难看出,这些更改的做出经过了深思熟虑和激烈讨论。本系列所展示的这些主题旨在让普通的 Python 程序员无需遍阅所有的 PEP 就能立即对这些更改有大致的概念。
标签:des style http io ar os 使用 java for
原文地址:http://www.cnblogs.com/Simon-xm/p/4069281.html