Python3初探,第2部分:高级主题有关Python版本3—,也即Python3000或Py3K—的前一篇文章讨论了Python内打破向后兼容性的一些基本变化,比如新的print()函数、bytes数据类型以及string类型的变化。本文是该系列文章的第2部分,探究了更为高深的一些主题,比如抽象基类(ABC)、元类、函数注释和修饰符(decorator)、整型数(integerliteral)支持、数值类型层次结构以及抛出和捕获异常,其中的大多数特性仍然会打破与版本2x产品线的向后兼容性。类修饰符在Python之前的版本中,对方法的转换必须在方法定义之后进行。对于较长的方法,此要求将定义的重要组成部分与PythonEnhancementProposal(PEP)318(有关链接,请参见参考资料)给出的外部接口定义分离。下面的代码片段演示了这一转换要求:清单1.Python3之前版本中的方法转化defmyMethod(self):#dosomethingmyMethod=transformMethod(myMethod)为了让此类情景更易于读懂,也为了避免必须多次重用相同的方法名,在Python版本2.4中引入了方法修饰符。修饰符是一些方法,这些方法可以修改其他方法并返回一个方法或另外一个可调用对象。对它们的注释是在修饰符的名称前冠以“at”符号(@)—类似Java™注释的语法。清单2显示了实际应用中的修饰符。清单2.一个修饰符方法@transformMethoddefmyMethod(self):#dosomething修饰符是一些纯粹的语法糖(syntacticsugar)—或者(如Wikipedia所言)“对计算机语言语法的补充,这些补充并不影响语言的功能,而是会让语言变得更易于被人使用。”修饰符的一种常见用法是注释静态方法。比如,清单1和清单2相当,但清单2更容易被人读懂。定义修饰符与定义其他方法无异:defmod(method):method.__name__=Johnreturnmethod@moddefmodMe():passprint(modMe.__name__)更棒的是Python3现在不仅支持针对方法的修饰符,并且支持针对类的修饰符,所以,取代如下的用法:classmyClass:passmyClass=doSomethingOrNotWithClass(myClass)我们可以这样使用:@doSomethingOrNotWithClassclassmyClass:pass元类元类是这样一些类,这些类的实例也是类。Python3保留了内置的、用来创建其他元类或在运行时动态创建类的metaclass类型。如下的语法仍旧有效:aClass=type('className',(object,),{'magicMethod':lambdacls:print(blahblah)})上述语法接受的参数包括:作为类名的字符串、被继承对象的元组(可以是一个空的元组)和一个包含可以添加的方法的字典(也可以是空的)。当然,也可以从类型继承并创建您自己的元类:classmeta(type):def__new__(cls,className,baseClasses,dictOfMethods):returntype.__new__(cls,className,baseClasses,dictOfMethods)只允许关键字的参数Python3已经更改了函数参数分配给“参数槽(parameterslot)”的方式,可以在传递进的参数内使用星号(*)以便不接受可变长度的参数。命名的参数必须在*之后—比如,defmeth(*,arg1):pass。更多信息,请参考Python文档或PEP3102(有关链接,请参见参考资料)。注意:如果上面两个例子起不到任何作用,我强烈建议您阅读DavidMertz和MicheleSimionato合写的有关元类的系列文章。相关链接,请参见参考资料。请注意,现在,在类定义中,关键字参数被允许出现在基类列表之后—通常来讲,即classFoo(*bases,**kwds):pass。使用关键字参数metaclass将元类传递给类定义。比如:classaClass(baseClass1,baseClass2,metaclass=aMetaClass):pass旧的元类语法是将此元类分配给内置属性__metaclass__:classTest(object):__metaclass__=type而且,既然有了新的属性—__prepare__—我们就可以使用此属性为新的类名称空间创建字典。在类主体被处理之前,先会调用它,如清单3所示。清单3.使用the__prepare__attribute的一个简单元类defmeth():print(Callingmethod)classMyMeta(type):@classmethoddef__prepare__(cls,name,baseClasses):return{'meth':meth}def__new__(cls,name,baseClasses,classdict):returntype.__new__(cls,name,baseClasses,classdict)classTest(metaclass=MyMeta):def__init__(self):passattr='anattribute't=Test()print(t.attr)我们从PEP3115节选了一个更为有趣的例子,如清单4所示,这个例子创建了一个具有其方法名称列表的元类,而同时又保持了类方法声明的顺序。清单4.保持了类成员顺序的一个元类#Thecustomdictionaryclassmember_table(dict):def__init__(self):self.member_names=[]def__setitem__(self,key,value):#ifthekeyisnotalreadydefined,addtothe#listofkeys.ifkeynotinself:self.member_names.append(key)#Callsuperclassdict.__setitem__(self,key,value)#ThemetaclassclassOrderedClass(type):#Thepreparefunction@classmethoddef__prepare__(metacls,name,bases):#Nokeywordsinthiscasereturnmember_table()#Themetaclassinvocationdef__new__(cls,name,bases,classdict):#Notethatwereplacetheclassdictwitharegular#dictbeforepassingittothesuperclass,sothatwe#don'tcontinuetorecordmembernamesaftertheclass#hasbeencreated.result=type.__new__(cls,name,bases,dict(classdict))result.member_names=classdict.member_namesreturnresult在元类内所做的这些改变有几个原因。对象的方法一般存储于一个字典,而这个字典是没有顺序的。不过,在某些情况下,若能保持所声明的类成员的顺序将非常有用。这可以通过让此元类在信息仍旧可用时,较早地涉入类的创建得以实现—这很有用,比如在C结构的创建中。借助这种新的机制还能在将来实现其他一些有趣的功能,比如在类构建期间将符号插入到所创建的类名称空间的主体以及对符号的前向引用。PEP3115提到更改语法还有美学方面的原因,但是对此尚存在无法用客观标准解决的争论(到PEP3115的链接,请参见参考资料)。抽象基类正如我在Python3初探,第1部分:Python3的新特性中提到的,ABC是一些不能被实例化的类。Java或C++语言的程序员应该对此概念十分熟悉。Python3添加了一个新的框架—abc—它提供了对ABC的支持。这个abc模块具有一个元类(ABCMeta)和修饰符(@abstractmethod和@abstractproperty)。如果一个ABC具有一个@abstractmethod或@abstractproperty,它就不能被实例化,但必须在一个子类内被覆盖。比如,如下代码:fromabcimport*classC(metaclass=ABCMeta):passc=C()这些代码是可以的,但是不能像下面这样编码:fromabcimport*classC(metaclass=ABCMeta):...@abstractmethod...defabsMethod(self):...passc=C()Traceback(mostrecentcalllast):Filestdin,line1,inmoduleTypeError:Can'tinstantiateabstractclassCwithabstractmethodsabsMethod更好的做法是使用如下代码:classB(C):...defabsMethod(self):...print(Nowaconcretemethod)b=B()b.absMethod()NowaconcretemethodABCMeta类覆盖属性__instancecheck__和__subclasscheck__,借此可以重载内置函数isinstance()和issubclass()。要向ABC添加一个虚拟子类,可以使用ABCMeta提供的register()方法。如下所示的简单示例:classTestABC(metaclass=ABCMeta):passTestABC.register(list)TestABC.__instancecheck__([])True它等同于使用isinstance(list,TestABC)。您可能已经注意到Python3使用__instancecheck__,而非__issubclass__,使用__subclasscheck__,而非__issubclass__,这看起来更为自然。若将参数isinstance(subclass,superclass)反转成,比如superclass.__isinstance__(subclass),可能会引起混淆。可见,语法superclass.__instancecheck__(subclass)显然更好一点。在collections模块内,可以使用几个ABC来测试一个类是否提供了特定的一个接口:fromcollectionsimportIterableissubclass(list,Iterable)True表1给出了这个集合框架的ABC。表1.这个集合框架的ABCABCInheritsContainerHashableIterableIteratorIterableSizedCallableSequenceSized,Iterable,ContainerMutableSequenceSequenceSetSized,Iterable,ContainerMutableSetSetMappingSized,Iterable,ContainerMutableMappingMappingMappingViewSizedKeysViewMappingView,SetItemsViewMappingView,SetValuesViewMappingView集合集合框架包括容器数据类型、双端队列(即deque)以及一个默认的字典(即defaultdict)。一