除了用户定义的函数,调用运算符(即 ())还可以应用到其他对象上。如果想判断对象能否调用,可以使用内置的 callable() 函数。Python 数据模型文档列出了 7 种可调用对象。
- 使用 def 语句或 lambda 表达式创建
- 内置函数:使用 C 语言(CPython)实现的函数,如 len 或 time.strftime。
- 内置方法:使用 C 语言实现的方法,如 dict.get。
- 方法:在类的定义体中定义的函数。
- 类:调用
类时会运行类的__new__方法创建一个实例,然后运行__init__方法,初始化实例,最后把实例返回给调用方。因为Python没有new运算符,所以调用类相当于调用函数。 - 类的实例: 如果类定义了
__call__方法,那么它的实例可以作为函数调用。 - 生成器函数: 使用
yield关键字的函数或方法。调用生成器函数返回的是生成器对象。
用户定义的可调用类型
不仅 Python 函数是真正的对象,任何 Python 对象都可以表现得像函数。为此,只需实现实例方法 __call__。
import randomclass BingoCage:def __init__(self, items):# __init__ 接受任何可迭代对象;在本地构建一个副本,防止列表参数的意外副作用。self._items = list(items)# shuffle 定能完成工作,因为 self._items 是列表。random.shuffle(self._items)def pick(self): # 起主要作用的方法。try:return self._items.pop()except IndexError: # 如果 self._items 为空,抛出异常,并设定错误消息。raise LookupError('pick from empty BingoCage')def __call__(self): # bingo.pick() 的快捷方式是 bingo()return self.pick()if __name__ == '__main__':bingo = BingoCage(range(3))print(bingo.pick())# 0print(bingo())# 2print(callable(bingo))# True
实现 __call__ 方法的类是创建函数类对象的简便方式,此时必须在内部维护一个状态,让它在调用之间可用,例如 BingoCage 中的剩余元素。装饰器就是这样。装饰器必须是函数,而且有时要在多次调用之间“记住”某些事 [ 例如备忘(memoization),即缓存消耗大的计算结果,供后面使用。
创建保有内部状态的函数,还有一种截然不同的方式——使用闭包