6.6. 全部放在一起
Description: Python from novice to pro
Keywords: Python, Dive Into Python, tutorial, object-oriented, programming, documentation, book, free

# 深入 Python :Dive Into Python 中文版 Python 从新手到专家 [Dip_5.4b_CPyUG_Release] | Find:

## 6.6. 全部放在一起

再一次，所有的多米诺骨牌都放好了。我们已经看过每行代码是如何工作的了。现在往回走一步，看一下放在一起是怎么样的。

### 例 6.21. `listDirectory`

    def listDirectory(directory, fileExtList):
        "get list of file info objects for files of particular extensions"
        fileList = [os.path.normcase(f)
                    for f in os.listdir(directory)]
        fileList = [os.path.join(directory, f)
                   for f in fileList
                    if os.path.splitext(f)[1] 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]

| `listDirectory` 是整个模块主要的有趣之处。它接收一个 dictionary (在我的例子中如 `c:\music\_singles\`) 和一个感兴趣的文件扩展名列表 (如 `['.mp3']`)，接着它返回一个类实例的 list ，这些类实例的行为像 dictionary，包含了在目录中每个感兴趣文件的元数据。并且实现起来只用了几行直观的代码。
| 正如在前一节我们所看到的，这行代码得到一个全路径名的列表，它的元素是在 `directory` 中有着我们感兴趣的文件后缀 (由 `fileExtList` 所指定的) 的所有文件的路径名。
| 老学校出身的 Pascal 程序员可能对嵌套函数感到熟悉，但大部分人，当我告诉他们 Python 支持嵌套函数时，都茫然地看着我。嵌套函数，从字面理解，是定义在函数内的函数。嵌套函数 `getFileInfoClass` 只能在定义它的函数 `listDirectory` 内进行调用。正如任何其它的函数一样，不需要一个接口声明或奇怪的什么东西，只要定义函数，开始编码就行了。
| 既然你已经看过 `os` 模块了，这一行应该能理解了。它得到文件的扩展名 (`os.path.splitext(filename)[1]`)，将其转换为大写字母 (`.upper()`)，从圆点处进行分片 (`[1:]`)，使用字符串格式化从其中生成一个类名。所以 `c:\music\ap\mahadeva.mp3` 变成 `.mp3` 再变成 `MP3` 再变成 `MP3FileInfo`。
| 在生成完处理这个文件的处理类的名字之后，我们查阅在这个模块中是否存在这个处理类。如果存在，我们返回这个类，否则我们返回基类 `FileInfo`。这一点很重要：这个函数返回一个类。不是类的实例，而是类本身。
| 对每个属于我们 “感兴趣文件” 列表 (`fileList`)中的文件，我们用文件名 (`f`) 来调用 `getFileInfoClass`。调用 `getFileInfoClass(f)` 返回一个类；我们并不知道确切是哪一个类，但是我们并不关心。接着我们创建这个类 (不管它是什么) 的一个实例，传入文件名 (又是 `f`) 给 `__init__` 方法。正如我们在本章的前面所看到的，`FileInfo` 的 `__init__` 方法设置了 `self["name"]`，它将引发 `__setitem__` 的调用，而 `__setitem__` 在子类 (`MP3FileInfo`) 中被覆盖掉了，用来适当地对文件进行分析，取出文件的元数据。我们对所有感兴趣的文件进行处理，返回结果实例的一个 list。

请注意 `listDirectory` 完全是通用的。它事先不知道将得到哪种类型的文件，也不知道哪些定义好的类能够处理这些文件。它检查目录中要进行处理的文件，然后反观本身模块，了解定义了什么特别的处理类 (像 `MP3FileInfo`)。你可以对这个程序进行扩充，对其它类型的文件进行处理，只要用适合的名字定义类：`HTMLFileInfo` 用于 HTML 文件，`DOCFileInfo` 用于 Word `.doc` 文件，等等。不需要改动函数本身， `listDirectory` 将会对它们都进行处理，将工作交给适当的类，接着收集结果。