python面向对象编程
示例代码
""" Framework for getting filetype-specific metadata. Instantiate appropriate class with filename. Returned object acts like a dictionary, with key-value pairs for each piece of metadata. import fileinfo info = fileinfo.MP3FileInfo("/music/ap/mahadeva.mp3") print "\\n".join(["%s=%s" % (k, v) for k, v in info.items()]) Or use listDirectory function to get info on all files in a directory. for info in fileinfo.listDirectory("/music/ap/", [".mp3"]): ... Framework can be extended by adding classes for particular file types, e.g. HTMLFileInfo, MPGFileInfo, DOCFileInfo. Each class is completely responsible for parsing its files appropriately; see MP3FileInfo for example. """ import os import sys from UserDict import UserDict def stripnulls(data) " strip whitespace and nulls " return data.replace( " \00 " , "" ).strip() class FileInfo(UserDict): " store file metadata " def __init__ (self, filename= None): UserDict. __init__ (self) self[ " name " ] = filename class MP3FileInfo(FileInfo): " store ID3v1.0 MP3 tags " tagDataMap = { " title " : ( 3, 33 , stripnulls), " artist " : ( 33, 63 , stripnulls), " album " : ( 63, 93 , stripnulls), " year " : ( 93, 97 , stripnulls), " comment " : ( 97, 126 , stripnulls), " genre " : (127, 128 , ord)} def __parse (self, filename): " parse ID3v1.0 tags from MP3 file " self.clear() try : fsock = open(filename, " rb " , 0) try : fsock.seek( -128, 2 ) tagdata = fsock.read(128 ) finally : fsock.close() if tagdata[:3] == " TAG " : for tag, (start, end, parseFunc) in self.tagDataMap.items(): self[tag] = parseFunc(tagdata[start:end]) except IOError: pass def __setitem__ (self, key, item): if key == " name " and item: self. __parse (item) FileInfo. __setitem__ (self, key, item) def listDirectory(directory, fileExtList): " get list of file info objects for files of particular extensions " fileExtList = [ext.upper() for ext in fileExtList] fileList = [os.path.join(directory, f) for f in os.listdir(directory) \ if os.path.splitext(f)[1].upper() in fileExtList] def getFileInfoClass(filename, module=sys.modules[FileInfo. __module__ ]): " get file info class from filename extension " subclass = " %sFileInfo " % os.path.splitext(filename)[1].upper()[1 :] return hasattr(module, subclass) and getattr(module, subclass) or FileInfo return [getFileInfoClass(f)(f) for f in fileList] if __name__ == " __main__ " : for info in listDirectory( " /music/_singles/ " , [ " .mp3 " ]): print " \n " .join([ " %(key)s=%(value)s " % vars() for key, value in info.items()]) print
修改目录路径到自己机器的MP3文件路径
面向对象综述:模块,类,函数,文档字符串,读代码找到总体印象,Python是完全面向对象的。 定义类:可以自己定义类,从自己的或者是内置的类继承,然后对类进行实例化;只要定义类,然后编码就行了;类以保留子class开始,跟着是类的名字; 最简单的python类
class foo: # 类的名字,没有从任何地方继承。 pass # python保留字,是一条什么都不做的语句,删除函数或者类的时候是一个很好的占位符
实际上大多数的类都是继承而来的,然后定义自己的类方法和属性,Python 有一个同c++构造器类似的东西__init__方法。
定义FIleInfo类
from UserDict import UserDict class FileInfo(UserDict): # FileInfo是从UserDict类继承而来的,是一个像字典一样的动作的类,允许完全子类化字典数据类型,同时增加自己的行为 " store file metadata " # 文档字符串 def __init__ (self, filename=None): # __init__在类的实例创建后即被调用,可能会诱使你,但是不正确的,叫它为类的构造器。看上去像是构造器,是类第一个定义的方法,在一个新创建的类实例中,它是首先被执行的代码,不正确的是,是一位对象在__init__被调用的时候已经被构造出来了,已经有了对类的新实例一个有效的引用。 #self,每一个类的方法的第一个参数,都是指向了类的当前实例的一个引用,按照习惯被定义成self。像c++中的this,__init__中self指向新创建的对象,在其他方法中,他指向方法被调用的对象。 #__init__方法可以接受任意个数的参数,就像函数一样,参数可以用缺省值定义,可以设置成对于调用者可选 UserDict. __init__ (self) # 一些伪面向对象语言,父类的方法在子类的方法执行前被自动调用,Python不行,必须要显示调用在父类中合适的方法。而且调用父类的方法的时候要包含参数self,跟着类方法所接受的任何其他类型。此处,除了self没有使用其他参数调用UserDict类的__init__方法 self[ " name " ] = filename # 这个类像字典一样动作,将参数filename赋值给对象name关键字,作为它的值。init方法不返回任何东西。
每个类的方法的第一个参数都命名为self,是一个强烈的习惯。
__init__方法是可选的,但是一旦定义了一个,就必须记得显示调用父类的__init__方法。注意:任何一个子类想要扩展父类的行为,后代方法必须在适当的时机,使用适当的参数,显示的调用父类的方法。
类的实例化:实例化一个类,只需要简单的调用它,传递给它的是__init__函数中的参数。返回值将是新创建的对象,没有c++中的new操作符。 FileInfo实例(定义在fileinfo模块里)
>>> import fileinfo >>>f = fileinfo.FileInfo( " /media/shi.mp3 " ) # 创建一个实例,赋给变量f,传入一个参数,即__init__里边的filename >>> f. __class__ # 对象的类是内置属性 < class fileinfo.FileInfo at 010EC204> >>> f. __doc__ # 一个类的所有实例共享一个字符串 ' base class for file info ' >>> f # init将文件名参数赋值给self['name'],传递的参数从右向左发送给init方法 { ' name ' : ' /media/shi.mp3 ' }
不需要显示的释放实例,应为当它们赋值给的变量超出作用域的时候,自动的释放,内存泄露在python中很少见。
尝试内存泄露
>>> def leakmem(): ... f = fileinfo.FileInfo( ' /shi.mp3 ' ) # 每次leakmen被调用,创建一个实例给变量f,这个变量是函数内的一个局部变量。然后函数结束没有释放f,认为有泄露,但是不是,函数结束的时候f已经释放掉了 ... >>> for i in range(100): # 每一次调用leakmem函数,决不会泄露内存,每一次,python将在leakmem返回前破坏掉新创建的FileInfo类。 ... leakmem()
对于这种垃圾收集的方式,技术上的术语叫做‘引用计数’。python维护着为每一个实例创建的实例的引用列表,上边对实例的引用只有一个f,函数结束的时候,f超出了作用域,所以引用计数变成0,python自动破坏实例。
UserDict:一个封装类 在UserDict模块中的UserDict类,是FileInfo类的父类,没什么特别的。 不能子类化字符串,列表,字典的内置数据类型,作为补充,提供了封装类,可以模拟这些内置数据类型的行为,UserString,UserList,UserDict通过使用一个由普通和专用方法组成的联合体,UserDict是一个字典的出色模仿品,仅仅是一个类它,可以对它进行子类化,提供可定制的字典的类,比如,FileInfo。
class UserDict: # 是一个基类,不是继承而来 def __init__ (self, dict=None): #2 self.data = {} #3 if dict is not None: self.update(dict) #4
2,FIleInfo中覆盖了的 __init__方法。这个父类的参数与子类的不同,有的语言支持通过参数列表的函数重载,也就是,同名的多个函数,有着不同个数的参数,或者不同类型的参数。另外的语言甚至支持通过参数名的重载,也就是,同名的多个函数,有相同个数相同类型的参数,但是参数名字不同。这个python 都是不支持的,不管怎么样都没有函数重载的方式。一个__init__方法就是一个__init__方法,不管它有什么样的参数,每个类只能拥有一个__init__方法,如果一个子类拥有一个__init__方法,它总是覆盖父类的__init__方法,甚至子类可以用不同的参数列表来定义它。3,python 支持数据属性即数据成员,它是由某个特定的实例所拥有的属性,要从类外引用这个名字需要在名字前边加上实例的名字来限制它,instanc.data,限定的方法与你用模块的名字来限定函数一样。要在类的内部引用一个数据属性,使用self作为限定符。为了方便,所有的数据属性的值都在__init__方法中初始化为有意义的值,然而这并不是必须的,因为数据属性,像局部变量一样,首次赋值给它的时候突然产生。4,当在一个块中仅有一条语句的一个简写,也可以用缩进代码,但是不能混用。
UserDict普通方法
def cleaf(self): self.data.clear() # 1 def copy(self): # 2 if self. __class__ is UserDict: # 3 return UserDict(self.data) import copy # 4 return copy.copy(self) def keys(self): return self.data.keys() # 5 def items(self): return self.data.items() def values(self): return self.data.values()
1,python类中的普通方法,python 不会替你调用,只是像类的一般方法一样调用,类封装的基本技术:保存一个真正的字典作为数据属性,定义所有的字典有拥有的方法,并且将每个类方法重定向到真正字典上的响应方法。 (clear方法删除它所有关键字和对应的值)。 2,真正的字典copy方法会返回一个新的字典,是原始字典原样的复制。但是不能简单的重定向到self.data.copy,因为那个方法返回的是一个真正的字典,而我们想返回的是同一个类的一个新的实例,就像是self。3, 使用__class__属性来查看self是否是一个userdict,如果是 就创建一个新的userdict实例返回,并传给他真正的字典,这个字典已经放在了self.data中了。4,self.__class__不是Userdict,那么self一定是userdict的某个子类,userdict不知道怎么生成一个它的子类的一个原样的拷贝,有可能在子类中定义了一些新的数据属性,所以我们只能完全拷贝他们,确定拷贝了他们的全部内容。幸运的是,python的一个模块可以正确的完成这件事情,叫做copy,copy能够拷贝任何的python对象return copy.copy(self)。 5,其他的方法直接重定向到self.data的内置函数上去。
专用类方法 除了普通的类方法,还要有一些对于Python类可以定义的专用方法,专用方法是在特殊情况下或者是当使用特别的语法时候python替你调用的,而不是在代码中直接调用,上一节中,普通的方法对在类中封装字典很有帮助。但是只有普通方法是不够的,因为除了字典调用方法之外,还有很多事情可以做,例如,可以通过一种没有包含显示方式调用的语法来得到和设置数据项,这就是专用方法产生的原因:提供了一种方法,可以将非方法调用影射到方法调用上。 __getitem__专用方法
def __getitem__ (self, key): return self.data[key] >>f = fileinfo.FileInfo( " /music/ka.mp3 " ) >> f { " name " : " /music/ka.mp3 " } >>f. __getitem__ ( " name " ) # 这个方法只是重定向到字典,返回了字典的值,但是怎么调用它呢,可以直接调用这个方法,但是实际的操作过程当中不会那样做,在这里执行只是告诉怎么工作的,正确的调用__getitem__是python替我们调用的 " music/ka.mp3 " >>f[ " name " ] # 这个看上去像一个得到字典值的语法,事实上它返回你期望的值。暗地里边python已经转化了过程。其实是:f.__getitme__("name")的方法调用。这就是为什么说是专用的类方法,可以自己调用也可以python帮助我们调用, " music/ka.mp3 "__setitem__专用方法
def __setitem__ (self, key, item): self.data[key]= item >>> f { ' name ' : ' /music/_singles/kairo.mp3 ' } >>> f. __setitem__ ( " genre " , 31) # 通常不会直接调用的 >>> f { ' name ' : ' /music/_singles/kairo.mp3 ' , ' genre ' :31 } >>> f[ " genre " ] = 32 # 暗地调用了上边的方法 >>> f { ' name ' : ' /music/_singles/kairo.mp3 ' , ' genre ' :32}
__setitem__是一个专用类方法,也仍然是一个类方法,在userdict中定义__setitem__方法一样容易,我们可以在子类中重新对它进行定义,对父类的方法进行重新覆盖,允许我们定义出某些方面像字典的类,但是我们可以自己定义它的行为,超过和超出内置的字典。 这个是学习的整个框架的基础 。每个文件类型可以拥有一个处理器类,这些类知道如何从一个特殊的文类型得到元数据。一旦知道了某些属性比如文件名和位置,处理器类就知道如何自动的得到其他的属性。它的实现是通过覆盖__setitem__方法,检查特别的关键字,然后当找到后加入额外的处理。
在MP3FileInfo中覆盖__setitem__:MP3FileInfo是FileInfo的一个子类,在设置一个MP3FileInfo的name时候,并不只是设置了name关键字(父类中这么做的),它还要在文件自身内部进行搜索MP3的标记然后填充一整套关键字集合。
def __setitem (self, key, item): # 进行定义的时候要严格按照父类方法相同的形式定义,技术上说,参数的名字没有关系,只是个数 if key == ' name ' and item # 如果想给name赋值我们还想做额外的事情。 self. __parse (item) # 对name所作的额外处理封装在了__parase方法中,这是定义在MP3FileInfo中的另一个类的方法, 我们调用它的时候,使用self对其限定,否则仅仅是调用__parse将只会看成定义在类中的一个类方法,当然用同样的方法来引用数据属性 FileInfo. __setitem__ (self, key, item) # 做完额外的处理时后,需要调用父类的方法,Python中不会自动完成,需要手工进行执行,尽管没有一个直接的类方法,我们还是直接用父类调用,Python会沿着父类树走,直到它找到一个有着我们正在调用方法的类,这行代码最终会找到并且调用定义在userdict中的__setitem__
在一个类中存取数据类型的时候,需要限定属性名字:self.attribute, 调用类中的其他方法的时候,需要限定方法:self.method。
设置一个MP3FileInfo的name
>>> import fileinfo >>> mp3file = fileinfo.MP3FileInfo() # 创建一个实例,但是没有传递问键名字,应为__init__方法中的filename参数是可选的,MP3FileInfo中没有__init__方法,沿着父类树走,在FileInfo中的__init__方法,这个__init__方法手工调用了父类的__init__方法,然后设置name关键字为filename,它为none。因为我们没传入一个文件名,所以mp3file最初看上去像有关键字 >>> mp3file { ' name ' :None} >>> mp3file[ " name " ] = " /music/_singles/kairo.mp3 " # 这个时候设置name关键字触发了MP3FileInfo的__setitem__(不是userdict),这个方法我们用一个真实的值来设置name关键字,接着调用self.__parse方法。通过输出可以看到,设置了其他的几个关键字 >>> mp3file { ' album ' : ' Rave Mix ' , ' artist ' : ' ***DJ MARY-JANE*** ' , ' genre ' : 31 , ' title ' : ' KAIRO****THE BEST GOA ' , ' name ' : ' /music/_singles/kairo.mp3 ' , ' year ' : ' 2000 ' , ' comment ' : ' http://mp3.com/DJMARYJANE ' } >>> mp3file[ " name " ] = " /music/_singles/sidewinder.mp3 " # python 调用__setitem__,__setitem__调用__parse,self.parse设置其他所有的关键字。 >>> mp3file { ' album ' : '' , ' artist ' : ' The Cynic Project ' , ' genre ' : 18, ' title ' : ' Sidewinder ' , ' name ' : ' /music/_singles/sidewinder.mp3 ' , ' year ' : ' 2000 ' , ' comment ' : ' http://mp3.com/cynicproject '}高级专用类方法:刚才的基础上还有更多的专用函数,模拟出许多甚至可能不知道的功能 在UserDict中更多的专用方法
def __repr__ (self): return repr(self.data) # 返回一个对象的字符串表示。可以用再任何对象上,而不仅仅是实例, def __cmp__ (self, dict): # 在比较类实例的时候被调用,通常可以使用==比较任意两个Python对象,不只是实例。有一些规则,定义何时内置数据类型被认为是相等的,例如,字典再有着相同的关键字和值的时候是相等的。对于类实例可以自己编写比较逻辑,然后可以使用==来比较你的类,python将会替你调用写的__cmp__专用方法。 if isinstance(dict, UserDict): return cmp(self, data, dict.data) def __len__ (self): return len(self.data) # 返回一个对象的长度,是一个内置的函数len(instance),字典的len是它的关键字的个数,列表或者序列的是元素的个数,对于类实例,定义len方法,可以自己编写长度的计算,然后调用len(instance),python替你调用__len__方法。 def __delitem (self, key): del self.data[key] # 从字典中删除单个元素的方法,调用 del instance[key]。所有的这些操作 只是为了在类中做一些我可以对一个内置数据类型所作的操作,不错,如果你能够从像字典一样的内置数据类型进行继承的话,事情就容易多了,但是也许可以,专用方法仍然是有用的,因为他们可以用给任何类,而不是只像userdict的封装类。 专用方法 意味着,任何类可以像字典一样保存键-值对,只要定义__setitem__对任何类可以表现的像一个序列,只要通过定义__getitem__方法,任何定义了__cmp__方法的类可以用==进行比较,并且如果你的类表现拥有类似长度的东西,不要定义GetLength方法,而定义__len__方法,使用len(instance)。 存在许多其他的专用方法 。有一整套的专用方法,可以让类表现得像数值一样,允许你在类上进行加,减,和执行其他数学操作,__call__方法让一个类表现的像一个函数,允许你直接调用一个类实例,并且存在其他的专用函数,允许类只有读或者写的数据属性。 类属性:数据属性是被一个特定类定例所拥有的变量。python也支持类属性,由类本身拥有的。 类属性介绍
class MP3FileInfo(FileInfo): " store ID3v1.0 MP3 tags " tagDataMap = { " title " : ( 3, 33 , stripnulls), " artist " : ( 33, 63 , stripnulls), " album " : ( 63, 93 , stripnulls), " year " : ( 93, 97 , stripnulls), " comment " : ( 97, 126 , stripnulls), " genre " : (127, 128 , ord)} >>> import fileinfo >>>fileinfo.MP3FileInfo # MP3FileInfo是类本身不是任何类实例 < class fileinfo.MP3FileInfo at 01257fdc >>>fileinfo.MP3FileInfo.tagDataMap # tagDataMap是类属性,是创建任何实例之前就有效了。 { ' title ' : (3, 33, <function stripnulls at 0260C8D4> ), ' genre ' : (127, 128, <built- in function ord> ), ' artist ' : (33, 63, <function stripnulls at 0260C8D4> ), ' year ' : (93, 97, <function stripnulls at 0260C8D4> ), ' comment ' : (97, 126, <function stripnulls at 0260C8D4> ), ' album ' : (63, 93, <function stripnulls at 0260C8D4> )} >>>m = fileinfo.MP3FileInfo() # 类属性既可以通过直接对类的引用,也可以通过对类的任意实例的引用来使用 >>> m.tagDataMap { ' title ' : (3, 33, <function stripnulls at 0260C8D4> ), ' genre ' : (127, 128, <built- in function ord> ), ' artist ' : (33, 63, <function stripnulls at 0260C8D4> ), ' year ' : (93, 97, <function stripnulls at 0260C8D4> ), ' comment ' : (97, 126, <function stripnulls at 0260C8D4> ), ' album ' : (63, 93, <function stripnulls at 0260C8D4>)}
类属性可以作为类级别的常量来使用,这就是为什么我们在MP3FileInfo中使用他们, 但是他们不是真正的常量,可以修改他们 。
修改类属性
>>> class counter: ... count = 0 # count是counter类的一个类属性 ... def __init__ (self): ... self. __class__ .count += 1 # __class__是每个类实例的一个内置属性,也是每个类的。它是一个类的引用,而self是一个类的实例。 ... >>> counter < class __main__ .counter at 0x84a929c> >>> counter.count # count是一个类属性,可以在创建任何类实例前通过直接对类的引用而得到 0 >>> c = counter() # 创建一个类实例会调用__init__方法,方法中会给类属性count加1,这样会影响到类本身,不只是新创建的实例。 >>> c.count 1 >>> counter.count 1 >>> d = counter() # 创建第二个实例会再次增加类属性count,类属性被类和所有类实例所共享,有些语言叫做共享变量。 >>> d.count 2 >>> c.count 2 >>> counter.count 2私有函数 python也有私有函数的概念,私有函数不可以在他们的模块外边被调用,私有类方法,不能够从他们的类外面被调用,私有属性不能从他们的类外边被使用,一个python函数,方法,属性是私有还是公有,完全取决于它的名字。 在MP3FileInfo中有两个方法,__parse和__setitem__。正如我们已经通过的,__setitem__是一个专有方法,通常,不直接调用它而是通过一个类上使用字典语法来调用它,但它是公有的,并且如果有一个好的理由,可以直接调用它(甚至在fileinfo模块的外边),但是,__parse是私有的,因为它的名字前边有两个下划线。 如果一个Python函数的名字,类方法,或属性以两个下划线开始(但不是结束),它是私有的;其他所有的都是公有的。 在python中,所有的专用方法(__setitem__)或者内置属性(像__doc__)遵守一个标准的命名习惯:开始和结束都有两个下划线,不要对自己的方法和属性用这种方法命名,后便只会搞乱你。 python没有类方法保护的概念,只能用于他们自己类和子父类中,类方法要不私有(只能在自己类中使用)要不公有(任何地方都可以使用) 尝试调用一个私有方法
>>> import fileinfo >>>m = fileinfo.MP3FileInfo() >>>m. __parse ( " /musi/shi.mp3 " ) # 试图调用一个类的私有方法,引发一个有些误导的异常,称那个方法不存在,它确实存在,但是它是私有的,在类外是不可使用的 Traceback (innermost last): File " <interactive input> " , line 1, in ? AttributeError: ' MP3FileInfo ' instance has no attribute ' __parse '严格的苏和哦,私有方法在类外是有效的,只是不容易处理。在Python中没有什么是真正私有的。 你可以通过 _MP3FileInfo__parse 名字来使用 MP3FileInfo 类的 __parse 方法。知道了这个方法很有趣,然后要保证决不在真正的代码中使用它。私有方法由于某种原因而私有,但是象其它很多在Python中的东西一样,它们的私有化基本上是习惯问题,而不是强迫的。 处理异常:python具有异常处理,通过使用try...except块。 标准python库中每个模块都有使用他们,python自己是在不同的情况下引发他们 使用不存在的字典关键字引发:keyerror异常 搜索列表中不存在的值引发:valueerror异常 调用不存在的方法:attributeerror异常 引用不存在的变量:nameerror异常 未强制转换就混合数据类型:typeerror异常 这些情况下我们都是简单的使用了python ide:一个错误发生,异常被打印出来,并且就是这些,传出一些调试信息然后程序终止,但是如果发生在真正的Python程序运行的时候,真个程序将会终止。 一个异常不一定引起程序的完全崩溃,异常引发时候,可以处理掉,有的时候异常是因为代码中的bug但是,许多时候,一个异常是可以预计的,如果知道一行代码会引发异常(打开一个不存在的文件,连到一个可能不能处理的数据库),应该使用一个try except块来处理异常。 打开一个不存在的文件
>>>fsock = open( " /notthere " , " r " ) # 文件不存在 引发IOError异常 Traceback (innermost last): File " <interactive input> " , line 1, in ? IOError: [Errno 2] No such file or directory: ' /notthere ' >>> try : ... fsock = open( " /nottehrer " ) ... except IOError: # 捕捉异常接着执行我们自己的代码块,这个代码快打出自己定义的代码 ... print " The file does not exist, exiting gracefully " ... print " This line will always print " # 异常被处理,在块后继续执行最后一样总是打印出来,无论异常是否发生。 The file does not exist, exiting gracefully This line will always print异常看上去不友好,不能捕捉异常,整个程序将崩溃,但是考虑一下别的方法,宁愿找回对于不存在文件的不可用的文件对象么?不管怎么样都要检查它的有效性,而且如果你忘记了,程序将会在下面某个地方给出奇怪的错误,这样就的追到源程序,使用异常,一旦发生错误,就可以找到问题源头。 异常处理还有许多其他的用处,在标准python库中的一个普通的方法就是试着导入一个模块,然后检查他是否能使用,导入一个并不存在的模块将引发一个importerror异常,使用这种方法来定义许多级别的功能,依靠在运行的时候哪个模块是有效的,支持多种平台(平台特定的代码被分离到不同的模块中) 支持特定平台功能,这个代码来源于getpass模块,一个从用户得到口令的封装模块。得到口令在UNIX,WINDOWS,和MACOS平台上的实现是不同的,但是这个代码封装了所有的不同
# Bind the name getpass to the appropriate function try : import termios, TERMIOS # termios 是个特定的模块,提供了对于输入终端的底层控制。如果模块无效,导入失败,引发异常。 except ImportError: try : import msvcrt # 试试msvcrt,十一个windows特的模块,可以提供在Microsoft Visual C++运行服务中的许多有用的函数的一个API。如果导入失败,Python会引发我们捕捉的 ImportError 异常。 except ImportError: try : from EasyDialogs import AskPassword # 如果前两个不能工作,我们试着从 EasyDialogs 导入一个函数,它是一个MacOS特定模块,提供了各种各样类型的弹出对话框。再一次,如果导入失败,Python会引发一个我们捕捉的 ImportError 异常。 except ImportError: getpass = default_getpass # 这些平台特定的模块没有一个有效(有可能,因为Python已经移植到了许多不同的平台上了),所以我们需要回头使用一个缺省口令输入函数(这个函数定义在 getpass 模块中的别的地方)。注意,我们在这里做的:我们将函数 default_getpass 赋给变量 getpass。如果你读了官方 getpass 文档,它会告诉你 getpass 模块定义了一个 getpass 函数。它是这样做的:通过绑定 getpass 到正确的函数来适应你的平台。然后当你调用 getpass 函数时,你实际上调用了平台特定的函数,是这段代码已经为你设置好的。你不需要知道或关心你的代码正运行在何种平台上;只要调用 getpass,则它总能正确处理。 else : getpass = AskPassword else : getpass = win_getpass else : # 一个 try...except 块可以有一条 else 子句,就象 if 语句。如果在 try 块中没有异常引发,然后 else 子句被执行。在本例中,那就意味着如果 from EasyDialogs import AskPassword 导入可工作,所以我们应该绑定 getpass 到 AskPassword 函数。其它每个 try...except 块有着相似的 else 子句,当我们找到一个 import 可用时,来绑定 getpass 到适合的函数 。 getpass = unix_getpass文件对象 python 有一个内置函数,open,用来打开磁盘上的文件,open返回一个文件对象,拥有一些方法和属性,可以得到打开文件的信息,和对打开的文件进行操作。 打开文件
>>> f = open( " /home/shishang/Interface.first " , " rb " ) # open接受三个参数,文件名,参数,缓冲区参数。只有一个参数是必须的就是文件名,这里以二进制方式打开文件读取 >>> f # open函数返回一个对象,一个文件对象有几个有用的属性 <open file ' /home/shishang/Interface.first ' , mode ' rb ' at 0xb76eff40> >>> f.mode # 告诉文件打开模式 ' rb ' >>> f.name # 文件的名字 ' /home/shishang/Interface.first '读取文件
>>> f <open file ' /music/_singles/kairo.mp3 ' , mode ' rb ' at 010E3988> >>> f.tell() # 打开的文件当前位置,文件没做任何事,当前位置是0,最开始处 0 >>> f.seek(-128, 2) # 0表示移动到一个绝对位置,1移到一个绝对位置,2表示对于文件尾的一个相对位置,我们使用2并且告诉文件对象从文件尾移动到128字节的位置, >>> f.tell() # 确认移到了当前位置, 7542909 >>> tagData = f.read(128) # read 方法从打开文件中读取指定个数的字节,并且返回含有读取数据的字符串。可选参数指定了读取的最大字节数。如果没有指定参数,read 将读到文件末尾。(我们本可以在这里简单地说一下 read(),因为我们确切地知道在文件的何处,事实上,我们读的是最后128个字节。)读出的数据赋给变量 tagData,并且当前的位置根据所读的字节数作了修改。 >>> tagData ' TAGKAIRO****THE BEST GOA ***DJ MARY-JANE*** Rave Mix 2000http://mp3.com/DJMARYJANE \037 ' >>> f.tell() # 确认了当前位置已经移动了。 7543037关闭文件
>>> f <open file ' /home/shishang/Interface.first ' , mode ' rb ' at 0xb76eff40> >>> f.closed # 文件对象的cloesd属性表示对象是否打开或关闭了文件,这里依然打开着,处理完毕,需要关闭文件 False >>> f.close() # 关闭文件,调用文件对象的close方法,这样就释放了在文件上的锁 >>> f <closed file ' /home/shishang/Interface.first ' , mode ' rb ' at 0xb76eff40> >>> f.closed True >>> f.seek(0) # 一旦文件关闭,可操作打开文件的方法没有一个可以使用,引发异常,文件被关闭,但是不意味着对象文件停止存在,变量f继续存在,直到超出作用域或者手工删除。 Traceback (most recent call last): File " <stdin> " , line 1, in <module> ValueError: I / O operation on closed file >>> f.tell() Traceback (most recent call last): File " <stdin> " , line 1, in <module> ValueError: I / O operation on closed file >>> f.read() Traceback (most recent call last): File " <stdin> " , line 1, in <module> ValueError: I / O operation on closed file >>> f.close() # 不会发生异常,静静的失败MP3FileInfo中的文件对象
try : # 打开和读取文件有风险,并且可能引发异常,所有这些都用一个try...except块封装 fsock = open(filename, " rb " , 0) # open 函数可能引发异常, try : fsock.seek( -128, 2) # seek方法可能引发异常 tagdata = fsock.read(128) # 可能引发异常 finally : # 新的finally模块,一旦文件被成功打开,我们应该绝对保证把它关闭,甚至由于seek或者read方法引发一个异常。try...finally可以用来:finally中的代码将总被执行,甚至某些东西在try块中引发一个异常也会执行。可以这样考虑,不管在路上发生什么,代码都会被即将灭亡的执行。 fsock.close() . . . except IOError: # 最后处理IO异常 ,它可能是由调用 open,seek,或 read 引发的 IOError 异常。这里,我们其实不关心,因为将要做的事就是静静地忽略它然后继续。(记住,pass 是一条不做任何事的Python语句。)这样完全合法,“处理”一个异常可以明确表示不做任何事。它仍然被认为处理过了,并且处理将正常继续,从 try...except 块的下一行代码。 passfor循环:在其他方面python太出色,通常不需要他们。 其它大多数语言没有象Python一样的强大的列表数据类型,所以你需要亲自做很多事情,指定开始,结束和步长,来定义一定范围的整数或字符或其它可重复的实体。但是在Python中, for 循环简单地在一个列表上循环,与 映射列表 的工作方式相同。
>>> li = [ ' a ' , ' b ' , ' e ' ] >>> for s in li: # for循环类似于映射列表 ... print s ... a b e >>> print " \n " .join(li) # 当你想要的只是一个join或是列表映射的时候,代替了for循环 a b e一次赋多个值:最酷的程序简写之一就是可以使用序列一次赋多个值 一次赋值多个
>>> v = ( ' a ' , ' b ' , ' e ' ) # v是一个三个元素的序列,(x,y,z)是一个三个变量的序列,依次赋值。 >>> (x, y, z) = v >>> x ' a ' >>> y ' b ' >>> z ' e '
构建可重用的模块的时候,经常给一个名字赋以一系列的值,C或者C++里边,经常用enum并且手工的列出每个常量和它对应的值,值是连续的时候显示的特别繁琐。python里边可以使用内置的range函数来迅速的给多个变量赋予连续值。
赋值连续
>>> range(7) # 内置的range是一个证书列表。接受一个上限 返回一个从0开始计数但是不包括上限的一个列表。 [0, 1, 2, 3, 4, 5, 6 ] >>> (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7) # MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,和 SUNDAY 是我们定义的变量。(这个例子来自 calendar 模块,一个有趣的用来打印日历的小模块,就象UNIX程序 cal。calendar 模块定义了星期每天的整数常量。) >>> MONDAY 0 >>> TUESDAY 1 >>> SUNDAY 6
使用这种技术,可以构建返回多值的函数,只要通过返回包含所有值的一个序列,调用者可以把返回值看成一个序列,或者将值赋给单个变量。
函数中返回多值
>>> import os # os.path操作文件路径的函数,split函数可以分割整个路径,返回一个包含路径和文件名的序列。 >>> os.path.split( " /music/ap/mahadeva.mp3 " ) ( ' /music/ap ' , ' mahadeva.mp3 ' ) >>> (filepath, filename) = os.path.split( " /music/ap/mahadeva.mp3 " ) >>> filepath ' /music/ap ' >>> filename ' mahadeva.mp3 ' >>> (shortname, extension) = os.path.splitext(filename) # 分割了一个文件名 >>> shortname ' mahadeva ' >>> extension ' .mp3 '
只要有可能,你最好使用在 os 和 os.path 中的函数来处理对文件,目录,和路径的操作。这些模块是对于平台特定模块的封装产物,所以象 os.path.split 之类的函数可以工作在UNIX,Windows,MacOS,和其它任何支持Python的平台上。利用多变量赋值甚至有更多可以做的,可以用在遍历一个序列列表的时候,意味着可将它用于for循环和列表映射当中,可能认为序列列表不是每天都要用到的东西,但是实际上字典的Items方法就返回一个序列列表,每个序列的形式为(key, value)。所以多变量赋值允许你通过简单的方法来遍历字典的元素。
遍历字典
>>> for k, v in os.environ.items() # os.environ 是一个在你的系统中所定义的环境变量的字典。在Windows下,它们是你的用户和系统变量,可以容易地从MS-DOS中得到。在UNIX下,它们是输出(export)到你的shell启动脚本的变量。在MacOS下,没有环境变量的概念,所以这个字典为空。 ... print " %s=%s " % (k, v) USERPROFILE =C:\Documents and Settings\mpilgrim OS = Windows_NT PROCESSOR_IDENTIFIER =x86 Family 6 Model 6 Stepping 10 , GenuineIntel COMPUTERNAME = MPILGRIM USERNAME =mpilgrim # os.environ.items() 返回一个序列列表:[(key1,value1),(key2,value2),...]。for 循环遍历这个列表。第一轮,它将 key1 赋给 k 而 value1 赋给 v,所以 k = USERPROFILE 而 v = C:\Documents and Settings\mpilgrim。第二轮,k 得到第二个关键字,OS,而 v 得到相对的值,Windows_NT。 […snip…]
使用多变量赋值不是绝对必需的。它是一种方便的简写,且可以让你的代码更加可读,特别是当处理字典时(通过 items 方法)。但是如果发现你迫使自已的代码通过种种周折(为了以正确的形式得到数据),只是为了让你可以一次给两个变量赋值,可能就不值得那么做了。
通过多变量赋值进行字典映射
>>> print " \n " .join([ " %s=%s " % (k, v) for k, v in os.environ.items()]) USERPROFILE =C:\Documents and Settings\mpilgrim OS = Windows_NT PROCESSOR_IDENTIFIER =x86 Family 6 Model 6 Stepping 10 , GenuineIntel COMPUTERNAME = MPILGRIM USERNAME = mpilgrim […snip…] # 多变量赋值也可以用于列表映射,使用这种简捷的方法来将字典映射成列表。本例中,我们通过将列表连接成一个字符串使得这种用法更深一步。注意它的输出与前例中的 for 循环一样。这就是为什么你在Python中看到那么少的 for 循环的原因;许多复杂的事情可以不用它们完成。你可以讨论是否这种方法更易读,但是它相当快,因为只有一条输出语句而不是许多。在MP3FileInfo中的多变量for循环
tagDataMap = { " title " : ( 3, 33 , stripnulls), " artist " : ( 33, 63 , stripnulls), " album " : ( 63, 93 , stripnulls), " year " : ( 93, 97 , stripnulls), " comment " : ( 97, 126 , stripnulls), " genre " : (127, 128 , ord)} . . . if tagdata[:3] == " TAG " : for tag, (start, end, parseFunc) in self.tagDataMap.items(): self[tag] = parseFunc(tagdata[start:end])
tagDataMap 是一个 类属性 ,它定义了我们正在一个MP3文件中所查找的标记。标记被保存在定长的字段中;一旦我们读出文件的最后128个字节,字节3到32是歌曲的题目,33-62是歌手名字,63-92是专集名字,等等。注意 tagDataMap 是一个序列字典,每个序列包含两个整数和一个函数引用。# 这个看上去有些复杂,其实不是。 for 变量结构与通过 items 返回的列表元素的结构相匹配。记住, items 返回一个形式为 ( key , value ) 的序列列表。列表的第一个元素是 ("title", (3, 33, <function stripnulls>)) ,所以循环的第一轮, tag 得到 "title" , start 得到 3 , end 得到 33 ,而 parseFunc 得到函数 stripnulls 。# 现在我们已经提取出了单个MP3标记的所有参数,保存标记数据很容易。我们从 start 到 end 划分 tagdata 以得到这个标记的实际数据,调用 parseFunc 来对数据进行后续处理,然后将它作为关键字的值赋给伪字典 self 的 tag 关键字。在遍历了 tagDataMap 中所有元素之后, self 拥有所有标记的值,并且 你知道那看上去象什么 。
“某度”空间 http://hi.baidu.com/new/wenjiashe521 cnblogs空间 http://www.cnblogs.com/wenjiashe521/
标签: 总结 , Python
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息