4.4. 通过 getattr 获取对象引用
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:

## 4.4. 通过 `getattr` 获取对象引用

你已经知道 Python 函数是对象。你不知道的是，使用 `getattr` 函数，可以得到一个直到运行时才知道名称的函数的引用。

### 例 4.10. `getattr` 介绍

    >>> li = ["Larry", "Curly"]
    >>> li.pop
    <built-in method pop of list object at 010DF884>
    >>> getattr(li, "pop")
    <built-in method pop of list object at 010DF884>
    >>> getattr(li, "append")("Moe")
    >>> li
    ["Larry", "Curly", "Moe"]
    >>> getattr({}, "clear")
    <built-in method clear of dictionary object at 00F113D4>
    >>> getattr((), "pop")
    Traceback (innermost last):
      File "<interactive input>", line 1, in ?
    AttributeError: 'tuple' object has no attribute 'pop'

| 该语句获取列表的 `pop` 方法的引用。注意该语句并不是调用 `pop` 方法；调用 `pop` 方法的应该是 `li.pop()`。这里指的是方法对象本身。
| 该语句也是返回 `pop` 方法的引用，但是此时，方法名称是作为一个字符串参数传递给 `getattr` 函数的。`getattr` 是一个有用到令人无法致信的内置函数，可以返回任何对象的任何属性。在这个例子中，对象是一个 list，属性是 `pop` 方法。
| 如果不确信它是多么的有用，试试这个：`getattr` 的返回值是 方法，然后你就可以调用它，就像直接使用 `li.append("Moe")` 一样。但是实际上你没有直接调用函数；只是以字符串形式指定了函数名称。
| `getattr` 也可以作用于字典。
| 理论上，`getattr` 可以作用于元组，但是由于元组没有方法，所以不管你指定什么属性名称 `getattr` 都会引发一个异常。

### 4.4.1. 用于模块的 `getattr`

`getattr` 不仅仅适用于内置数据类型，也可作用于模块。

### 例 4.11. `apihelper.py` 中的 `getattr` 函数

    >>> import odbchelper
    >>> odbchelper.buildConnectionString
    <function buildConnectionString at 00D18DD4>
    >>> getattr(odbchelper, "buildConnectionString")
    <function buildConnectionString at 00D18DD4>
    >>> object = odbchelper
    >>> method = "buildConnectionString"
    >>> getattr(object, method)
    <function buildConnectionString at 00D18DD4>
    >>> type(getattr(object, method))
    <type 'function'>
    >>> import types
    >>> type(getattr(object, method)) == types.FunctionType
    True
    >>> callable(getattr(object, method))
    True

| 该语句返回 `odbchelper` 模块中 `buildConnectionString` 函数的引用，第 2 章 第一个 Python 程序 你已经研习过这个方法了。(你看到的这个十六进制地址是我机器上的；你的输出结果会有所不同。)
| 使用 `getattr`，你能够获得同一函数的同一引用。通常，``getattr`(object, "attribute")` 等价于 `object.attribute`。如果 `object` 是一个模块的话，那么 `attribute` 可能是定义在模块中的任何东西：函数、类或者全局变量。
| 接下来的是你真正用在 `info` 函数中的东西。`object` 作为一个参数传递给函数； `method` 是方法或者函数的名称字符串。
| 在这个例子中，`method` 是函数的名称，通过获取 `type` 可以进行验证。
| 由于 `method` 是一个函数，所以它是可调用的。

### 4.4.2. `getattr` 作为一个分发者

`getattr` 常见的使用模式是作为一个分发者。举个例子，如果你有一个程序可以以不同的格式输出数据，你可以为每种输出格式定义各自的格式输出函数，然后使用唯一的分发函数调用所需的格式输出函数。

例如，让我们假设有一个以 HTML、XML 和普通文本格式打印站点统计的程序。输出格式在命令行中指定，或者保存在配置文件中。`statsout` 模块定义了三个函数：`output_html`、`output_xml` 和 `output_text`。然后主程序定义了唯一的输出函数，如下：

### 例 4.12. 使用`getattr` 创建分发者

    import statsout

    def output(data, format="text"):
        output_function = getattr(statsout, "output_%s" % format)
        return output_function(data)

| `output` 函数接收一个必备参数 `data`，和一个可选参数 `format`。如果没有指定 `format` 参数，其缺省值是 `text` 并完成普通文本输出函数的调用。
| 你可以连接 `format` 参数值和 "output_" 来创建一个函数名称作为参数值，然后从 `statsout` 模块中取得该函数。这种方式允许今后很容易地扩展程序以支持其它的输出格式，而且无需修改分发函数。所要做的仅仅是向 `statsout` 中添加一个函数，比如 `output_pdf`，之后只要将 “pdf” 作为 `format` 的参数值传递给 `output` 函数即可。
| 现在你可以简单地调用输出函数，就像调用其它函数一样。`output_function` 变量是指向 `statsout` 模块中相应函数的引用。

你是否发现前面示例的一个 Bug？即字符串和函数之间的松耦合，而且没有错误检查。如果用户传入一个格式参数，但是在 `statsout` 中没有定义相应的格式输出函数，会发生什么呢？还好，`getattr` 会返回 `None`，它会取代一个有效函数并被赋值给 `output_function`，然后下一行调用函数的语句将会失败并抛出一个异常。这种方式不好。

值得庆幸的是，`getattr` 能够使用可选的第三个参数，一个缺省返回值。

### 例 4.13. `getattr` 缺省值

    import statsout

    def output(data, format="text"):
        output_function = getattr(statsout, "output_%s" % format, statsout.output_text)
        return output_function(data)

| 这个函数调用一定可以工作，因为你在调用 `getattr` 时添加了第三个参数。第三个参数是一个缺省返回值，如果第二个参数指定的属性或者方法没能找到，则将返回这个缺省返回值。

正如你所看到，`getattr` 是相当强大的。它是自省的核心，在后面的章节中你将看到它更强大的示例。