Linux 版 (精华区)

发信人: emacs (被淹死的鱼), 信区: Linux
标  题: 第三章 面向对象框架 -- 1
发信站: 哈工大紫丁香 (Sat Jun 15 09:51:40 2002) , 转信



3.1. 接触
这一章,和此后的许多章,均讨论了面向对象的Python程序设计。还记得我说过你应该知
道一门面向对象语言来阅读这本书吗?哦,我可不是开完笑。


下面是一个完整的,可运行的Python程序。阅读模块,类,和函数的文档字符串,可以了
解这个程序所做的和如何工作的总体印象。

例 3.1. fileinfo.py

"""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 File
Info
    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.i
tems()])
        print  这个程序的输出要依赖于你硬盘上的文件。为了得到有意义的输出,你应
该修改目录路径指到你自已机器上的一个MP3文件目录。
 

例 3.2. fileinfo.py 的输出

下面就是从我的机器上得到的输出。你的输出将不一样,除非,由于某些令人吃惊的巧合
,你与我有着共同的音乐品味。

album=
artist=Ghost in the Machine
title=A Time Long Forgotten (Concept
genre=31
name=/music/_singles/a_time_long_forgotten_con.mp3
year=1999
comment=http://mp3.com/ghostmachine

album=Rave Mix
artist=***DJ MARY-JANE***
title=HELLRAISER****Trance from Hell
genre=31
name=/music/_singles/hellraiser.mp3
year=2000
comment=http://mp3.com/DJMARYJANE

album=Rave Mix
artist=***DJ MARY-JANE***
title=KAIRO****THE BEST GOA
genre=31
name=/music/_singles/kairo.mp3
year=2000
comment=http://mp3.com/DJMARYJANE

album=Journeys
artist=Masters of Balance
title=Long Way Home
genre=31
name=/music/_singles/long_way_home1.mp3
year=2000
comment=http://mp3.com/MastersofBalan

album=
artist=The Cynic Project
title=Sidewinder
genre=18
name=/music/_singles/sidewinder.mp3
year=2000
comment=http://mp3.com/cynicproject

album=Digitosis@128k
artist=VXpanded
title=Spinning
genre=255
name=/music/_singles/spinning.mp3
year=2000
comment=http://mp3.com/artists/95/vxp
3.2. 定义类

Python是完全面向对象的:可以定义自已的类,从自已的或内置类进行继承,然后对生成
的类进行实例化。

在Python中定义类很简单;就象定义函数,没有独立的定义接口。只要定义类,然后编码
就可以了。Python类以保留字 class 开始,跟着是类的名字。从技术上说,有这些就足够
了,因为一个类不需要从任何其它类继承而来。

例 3.3. 最简单的Python类

class foo: 
    pass      这个类的名字是 foo,它没有从任何其它的类继承而来。 
  这个类没有定义任何方法或属性,但是从语法上,需要在定义中有些东西,所以我们使
用了 pass。这是一个Python保留字,仅仅表示“向前走,不要往这看”。它相当于C++中
的大括号空集({})或者是Java中的单个分号。它是一条什么都不做的语句,当你删除函数
或类时,它是一个很好的占位符。
 
  你可能猜到了,在类中的每条语句都是缩排的,就象函数内的代码, if 语句, for 循
环,等等。第一条不缩近的语句不属于这个类。
 

当然,实际上大多数的类将从其它的类继承来的,并且它们将定义自已的类方法和属性。
但是如你所见,除了名字以外,没有什么东西是一个类必须拥有的。特别是,C++程序员可
能会感到奇怪,Python的类没有明确的构造器和析构器。Python类的确存在同一个构造器
相似的东西: __init__ 方法。

例 3.4. 定义 FileInfo 类

from UserDict import UserDict

class FileInfo(UserDict):              
    "store file metadata"              
    def __init__(self, filename=None):   
        UserDict.__init__(self)        
        self["name"] = filename        
                                         类的祖先被列在立即跟在类名字后面的小括
号里。所以 FileInfo 类是从 UserDict 类(这个类从 UserDict 中导入)继承而来。User
Dict 是一个象字典一样动作的类,它允许你完全子类化字典数据类型,同时增加你自已的
行为。(也存在相似的类 UserList 和 UserString ,它们允许你子类化列表和字符串。)
在这个类的背后有一些“巫术”,我们将在本章的后面,随着更进一步地研究 UserDict 
类,揭开这些秘密。
 
  类也可以有文档字符串,就象模块和函数。 
  __init__ 在类的实例创建后立即被调用。它可能会诱使你,但是不正确地,叫它为类的
构造器。说它诱使,是因为它看上去象构造器(按照习惯, __init__ 是类中第一个定义的
方法),象构造器一样动作(在一个新创建的类实例中,它是首先被执行的代码),并且听起
来也象构造器(“init”当然意味着构造的本性)。说它不正确,是因为对象在 __init__ 
被调用的时刻已经被构造出来了,你已经有了对类的新实例一个有效的引用。
 
  每一个类方法的第一个参数,包括 __init__,总是指向类的当前实例的一个引用。按照
习惯这个参数被命名为 self。(除了 self 不要把它命名为别的东西,这是一个非常强烈
的习惯。) self 象C++中的保留字 this。在 __init__ 方法中, self 指向新创建的对象
;在其它的方法中,它指向方法被调用的对象。
 
  __init__ 方法可以接受任意个数的参数,就象函数一样,参数可以用缺省值定义,可以
设置成对于调用者可选。在本例中, name 有一个缺省值 None,即Python的空值。
 
  一些伪面向对象语言,象Powerbuilder有一种“扩展”构造器和其它事件的概念,即父
类的方法在子类的方法执行前被自动调用。Python不这样做,你必须总是显示调用在父类
中适合的方法。同样,当调用父类方法时,你必须包含 self 参数,跟着类方法所接受的
任何其它类型。在本例中,我们没有使用参数(除了 self)调用 UserDict 类的 __init__
 方法。
 
  我对你说过,这个类的象字典一样动作,那么这里就是第一个印象。我们将参数 filen
ame 赋值给对象 name 关键字,作为它的值。
 
  注意 __init__ 方法从不返回一个值。 

噢。我知道有很多知识需要吸收,但是你要掌握它。所有的Python类以相同的方式工作,
所以一旦你学会了一个,就是学会了全部。再加上,在Python社区中有强烈的习惯,对于
每个类方法的第一个参数都命名为“self”,所以你不会浪费大量时间去习惯他人的风格
。如果你忘了别的任何事,也要记住这件事,因为我认定它会让你出错:

 
__init__ 方法是可选的,但是一旦你定义了一个,就必须记得显示调用父类的 __init__
 方法。这样更是正确的:何时一个子类想扩展父类的行为,后代方法必须在适当的时机,
使用适当的参数,显式调用父类方法。
 

3.3. 类的实例化

在Python中实际化类是很直接的。为了实例化一个类,只要简单地调用类,好象它是一个
函数,传递给它定义在 __init__ 模块中的参数。返回值将是新创建的对象。不象C++有显
式的 new 操作符。

例 3.5. 创建 FileInfo 实例

>>> import fileinfo
>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3") 
>>> f.__class__                                        
<class fileinfo.FileInfo at 010EC204>
>>> f.__doc__                                          
'base class for file info'
>>> f                                                  
{'name': '/music/_singles/kairo.mp3'}  我们在创建一个 FileInfo 类的实例(定义在
 fileinfo 模块里),将新创建的实例赋给变量 f。我们传入一个参数,/music/_singles
/kairo.mp3,它将结束就象在 FileInfo 中的 __init__ 方法的 filename 参数。
 
  每一个类的实例有一个内置属性, __class__,它是对象的类。(注意这个表示包括了在
我机器上的实例的物理地址,你的表示将是不一样的。)
 
  你可以存取实例的文档字符串,就象一个函数或模块。一个类的所有实例共享相同的文
档字符串。 
  还记得 __init__ 方法将它的文件名参数赋值给 self["name"]吗?哦,答案在这。当我
们创建类实例时,传递的参数从右向左发送给 __init__ 方法(跟着为对象的引用, self
,它是由Python自动添上去的)。
 

如果创建一个新的实例容易,破坏它们甚至更容易。通常,不需要显式地释放实例,因为
当它们所赋给的变量超出作用域时,它们会被自动地释放。内存泄漏在Python中很少见。


例 3.6. 尝试实现内存泄漏

>>> def leakmem():
...     f = fileinfo.FileInfo('/music/_singles/kairo.mp3') 
...     
>>> for i in range(100):
...     leakmem()                                            每次 leakmem 函数
被调用,我们创建 FileInfo 的一个实例,将其赋给变量 f,这个变量是函数内的一个局
部变量。然后函数结束未曾释放 f,所以你可能认为有内存泄漏,但是你错了。当函数结
束时,局部变量 f 超出了作用域。在这个地方,不再有任何对 FileInfo 新创建实例的引
用(因为除了 f 我们从未将其赋值给其它变量),所以Python替我们破坏掉实例。
 
  不管我们调用 leakmem 函数多少次,决不会泄漏内存,因为每一次,Python将在从 le
akmem 返回前破坏掉新创建的 FileInfo 类。
 

对于这种垃圾收集的方式,技术上的术语叫做“引用计数”。Python维护着对每一个创建
的实例的引用的列表。在上面的例子中,对于 FileInfo 实例只有一个引用:局部变量 f
。当函数结束时,变量 f 超出作用域,所以引用计数降为 0,则Python自动破坏实例。

在Python的以前版本中,存在引用计数失败的情况,并且Python不能在你后面进行清除。
如果你创建两个实例,它们相互引用(例如,双重链表,每一个结点有都一个指向列表中前
一个和后一个结点的指针),任一个实例都不会被自动破坏,因为Python(正确)认为对于每
个实例都存在一个引用。Python 2.0有一种额外的垃圾回收方式,叫做“标记后清除”,
它足够聪明,可以正确地清除循环引用。

由于以前学过的哲学课,让我困扰的是思考当事物没有人观察它的时候,它消失了,但是
这确实是在Python中所发生的。通常,你可以完全忘记内存管理,让Python在你后面进行
清除。

3.4. UserDict: 一个封装类

如你所见, FileInfo 是一个象字典一样动作的类。为了进一步揭示这一点,让我们看一
看在 UserDict 模块中的 UserDict 类,它是我们的 FileInfo 类的父类。没有什么特别
的,类是用Python写的,并且保存在一个 .py 文件里,就象我们的代码。特别的,它保存
在你的Python安装目录的 lib 目录下。


 
在Windows下的Python IDE中,你可以快速打开在你的库路径中的任何模块,使用 File->
Locate… (Ctrl-L)。
 

在Python中,你不能子类化象字符串,列表和字典的内置数据类型。作为补偿,Python附
带了封装类,可以模拟这些内置的数据类型的行为: UserString,UserList,和 UserDi
ct。通过使用一个由普通和专用方法组成的联合体, UserDict 确实是一个字典的出色模
仿品,但是它仅仅是一个类,同任何其它类一样,所以你可以对它进行子类化,以提供可
定制的象字典的类,如 FileInfo。

例 3.7. UserDict __init__ 方法

class UserDict:                                
    def __init__(self, dict=None):             
        self.data = {}                         
        if dict is not None: self.update(dict) 
  注意 UserDict 是一个基类,不是从任何其他类继承而来。 
  这就是我们在 FileInfo 类中进行了覆盖的 __init__ 方法。注意这个父类的参数列表
与子类不同。某些语言(如Powerbuilder)支持通过参数列表的函数重载,也就是,同名的
多个函数,有着不同个数的参数,或不同类型的参数。另外的语言(最明显如PL/SQL)甚至
支持通过参数名的重载,也就是,同名的多个函数,有相同个数相同类型的参数,但是参
数名字不同。Python不支持以上任何一种方式,不管怎么样它都没有函数重载的形式。一
个 __init__ 方法就是一个 __init__ 方法,不管它有什么样的参数。每个类只能有一个
 __init__ 方法,并且如果一个子类拥有一个 __init__ 方法,它总是覆盖父类的 __ini
t__ 方法,甚至子类可以用不同的参数列表来定义它。
 
  Python支持数据属性(在PoserBuilder中叫做“实例变量”,在C++中叫“数据成员”),
它是由某个特定的类实例所拥有的数据。在本例中,每个 UserDict 实例将拥有一个 dat
a 数据属性。要从类外的代码引用这个属性,需要用实例的名字限定它,instance.data,
限定的方法与你用模块的名字来限定函数一样。要在类的内部引用一个数据属性,我们使
用 self 作为限定符。为了方便,所有的数据属性都在 __init__ 方法中初始化为有意义
的值。然而,这并不是必须的,因为数据属性,象局部变量一样,当你首次赋给它值的时
候突然产生。
 
  这个语法你可能以前没看过(我还没有在这本书中的例子中用过它)。这是一条 if 语句
,但是没有在下一行有一个缩近块,而只是在冒号后面,在同一行上有单条语句。这完全
是合法的,它只是当你在一个块中仅有一条语句时的一个简写。(它就象在C++中没有用大
括号包括的单行语句。)你可以用这种语法,或者可以在后面的行拥有缩近代码,但是不能
对同一个块同时用两种方式。
 
 
应该总是在 __init__ 方法中给所有类的数据属性赋予一个初始值。这样做将会节省你在
后面调试的时间。 

例 3.8. UserDict 普通方法

    def clear(self): self.data.clear()          
    def copy(self):                             
        if self.__class__ is UserDict:          
            return UserDict(self.data)         
        import copy                             
        return copy.copy(self)                 
    def keys(self): return self.data.keys()     
    def items(self): return self.data.items()  
    def values(self): return self.data.values()
  clear 是一个普通的类方法,Python从不会替你调用,它只是象类的一般方法一样可用
,你可以在任何时候调用它。注意, clear 象所有的类方法一样(正常或专用的),使用 
self 作为它的第一个参数,但是当你调用方法时,不用包括 self ;这件事是Python为你
加上的。我知道这个有些迷惑人,但是你会完全习惯它的。还应注意这个封装类的基本技
术:保存一个真正的字典作为数据属性,定义所有真正字典有拥有的方法,并且将每个类
方法重定向到真正字典上的相应方法。(一旦你已经忘记了,字典的 clear 方法删除它的
所有关键字和关键字相应的值。)
 
  真正字典的 copy 方法会返回一个新的字典,它是原始字典的原样的复制(所有的键-值
对都相同)。但是 UserDict 不能简单地重定向到 self.data.copy,因为那个方法返回一
个真正的字典,而我们想要的是返回同一个类的一个新的实例,就象是 self。
 
  我们使用 __class__ 属性来查看是否 self 是一个 UserDict,如果是,太好了,因为
我们知道如何拷贝一个 UserDict:只要创建一个新的 UserDict ,并传给它真正的字典,
这个字典已经存放在 self.data中了。
 
  如果 self.__class__ 不是 UserDict,那么 self 一定是 UserDict 的某个子类(如可
能为 FileInfo),生活总是存在意外。 UserDict 不知道如何生成它的子类的一个原样的
拷贝,例如,有可能在子类中定义了其它的数据属性,所以我们只能完全复制它们,确定
拷贝了它们的全部内容。幸运的是,Python带了一个模块可以正确地完成这件事,它叫做
 copy。在这里我不想深入细节(然而它是一个绝对酷的模块,是否你想到要自已研究它了
呢?)。说 copy 能够拷贝任何Python对象就够了,这就是为什么我们在这里用它的原因。

 
  其余的方法是直截了当的重定向到 self.data 的内置函数上。
 
3.5. 专用类方法

除了普通的类方法,还有一些对于Python类可以定义的专用方法。专用方法是在特殊情况
下或当使用特别语法时由Python替你调用的,而不是在代码中直接调用(象普通的方法那样
)。

就象你在上一节所看到的,普通的方法对在类中封装字典很有帮助。但是只有普通方法是
不够的,因为除了对字典调用方法之外,还有很多事情可以做的。例如,你可以通过一种
没有包括显式方法调用的语法来得到和设置数据项。这就是专用方法产生的原因:它们提
供了一种方法,可以将非方法调用语法映射到方法调用上。

例 3.9. __getitem__ 专用方法

    def __getitem__(self, key): return self.data[key]>>> f = fileinfo.FileInfo
("/music/_singles/kairo.mp3")
>>> f
{'name':'/music/_singles/kairo.mp3'}
>>> f.__getitem__("name") 
'/music/_singles/kairo.mp3'
>>> f["name"]             
'/music/_singles/kairo.mp3'  __getitem__ 专用方法很简单。象普通的方法 clear,k
eys,和 values一样,它只是重定向到字典,返回字典的值。但是怎么调用它呢?哦,你
可以直接调用 __getitem__,但是在实际中你其实不会那样做:我在这里执行它只是要告
诉你它是如何工作的。正确地使用 __getitem__ 的方法是让Python来替你调用。
 
  这个看上去就象你用来得到一个字典值的语法,事实上它返回你期望的值。下面是隐藏
起来的一个环节:暗地里,Python已经将这个语法转化为 f.__getitem__("name") 的方法
调用。这就是为什么 __getitem__ 是一个专用类方法的原因,不仅仅是你可以自已调用它
,还可以通过使用正确的语法让Python来替你调用。
 

例 3.10. __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}  象 __getitem__ 方法一样, __
setitem__ 简单地重定向到真正的字典 self.data ,让它来进行工作。并且象 __getite
m__ 一样,通常你不会直接调用它,当你使用了正确的语法,Python会替你调用 __setit
em__ 。
 
  这个看上去象正常的字典语法,当然除了 f 实际上是一个类,它尽可能地打扮成一个字
典,并且 __setitem__ 是打扮的一个重点。这行代码实际上暗地里调用了 f.__setitem_
_("genre", 32)。
 

__setitem__ 是一个专用类方法,因为它可以让Python来替你调用,但是它仍然是一个类
方法。就象在 UserDict 中定义 __setitem__ 方法一样容易,我们可以在子类中重新定义
它,对父类的方法进行覆盖。这就允许我们定义出在某些方面象字典一样动作的类,但是
可以定义它自已的行为,超过和超出内置的字典。

这个概念是本章中我们正在学习的整个框架的基础。每个文件类型可以拥有一个处理器类
,这些类知道如何从一个特殊的文类型得到元数据。一但知道了某些属性(象文件名和位置
),处理器类就知道如何自动地得到其它的属性。它的实现是通过覆盖 __setitem__ 方法
,检查特别的关键字,然后当找到后加入额外的处理。

例如,MP3FileInfo 是 FileInfo 的子类。在设置了一个 MP3FileInfo 类的 name 时,并
不只是设置name 关键字(象父类 FileInfo 所做的),它还要在文件自身内进行搜索MP3的
标记然后填充一整套关键字集合。

例 3.11. 在 MP3FileInfo 中覆盖 __setitem__

    def __setitem__(self, key, item):         
        if key == "name" and item:            
            self.__parse(item)                
        FileInfo.__setitem__(self, key, item)   注意我们的 __setitem__ 方法严格
按照父类方法相同的形式进行定义。这一点很重要,因为Python将替你执行方法,则它希
望这个函数用确定个数的参数进行定义。(从技术上说,参数的名字没有关系,只是个数。
)
 
  这里就是整个 MP3FileInfo 类的难点:如果给 name 关键字赋一个值,我们还想做些额
外的事情。
 
  我们对 name 所做的额外处理封装在了 __parse 方法中。这是定义在 MP3FileInfo 中
的另一个类方法,则当我们调用它时,我们用 self 对其限定。仅是调用 __parse 将只会
看成定义在类外的普通方法,调用 self.__parse 将会看成定义在类中的一个类方法。这
不是什么新东西,你用同样的方法来引用数据属性。
 
  在做完我们额外的处理之后,我们需要调用父类的方法。记住,在Python中不会自动为
你完成,需手工执行。注意,我们在调用直接父类, FileInfo,尽管它没有一个 __seti
tem__ 方法。没问题,因为Python将会沿着父类树走,直到它找到一个有着我们正在调用
方法的类,所以这行代码最终会找到并且调用定义在 UserDict 中的 __setitem__。
 
 
当在一个类中存取数据属性时,你需要限定属性名:self.attribute。当调用类中的其它
方法时,你属要限定方法名:self.method。
 

例 3.12. 设置一个 MP3FileInfo 的 name

>>> import fileinfo
>>> mp3file = fileinfo.MP3FileInfo()                   
>>> mp3file
{'name':None}
>>> mp3file["name"] = "/music/_singles/kairo.mp3"      
>>> 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" 
>>> mp3file
{'album': '', 'artist': 'The Cynic Project', 'genre': 18, 'title': 'Sidewinder
', 
'name': '/music/_singles/sidewinder.mp3', 'year': '2000', 
'comment': 'http://mp3.com/cynicproject'}  首先,我们创建了一个 MP3FileInfo 的
实例,没有传递给它文件名。(我们可以不用它,因为 __init__ 方法的 filename 参数是
可选的。)因为 MP3FileInfo 没有它自已的 __init__ 方法,Python沿着父类树走,发现
了 FileInfo 的 __init__ 方法。这个 __init__ 方法手工调用了 UserDict 的 __init_
_ 方法,然后设置 name 关键字为 filename,它为 None,因为我们还没有传入一个文件
名。所以,mp3file 最初看上去象有一个关键字, name,它的值为 None 的字典。
 
  现在真正有趣的开始了。设置 mp3file 的 name 关键字触发了 MP3FileInfo 上的 __s
etitem__ 方法(不是 UserDict),这个方法注意到我们正在用一个真实的值来设置 name 
关键字,接着调用 self.__parse。尽管我们完全还没有研究过 __parse 方法,从它的输
出你可以看出,它设置了其它几个关键字:album,artist,genre,title,year,和 co
mment。
 
  修改 name 关键字将再次经受同样的处理过程:Python调用 __setitem__,__setitem_
_调用 self.__parse,self.__parse 设置其它所有的关键字。
 


--

※ 来源:.哈工大紫丁香 http://bbs.hit.edu.cn [FROM: 211.93.34.115]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:208.920毫秒