从本书一开始,就在使用“对象”这个术语,并且前面几章一直在学习 Python 内置对象。本章学习的函数,在 Python 中也是对象。也正是由于这个特点,使得 Python 中的函数有很多更优异的表现,减少了编程的苦恼——保持秀发。

图7-3-1 几种编程语言发明者
第一类对象(First-class Object)这个术语是英国的计算机科学家克里斯托弗·斯特雷奇(Christopher Strachey)于20世纪60年代提出来的,意指能够作为参数传给其他函数或者“存入”一个变量的对象(对于 Python 语言就是能够被变量引用的对象)。前面所学习过的数字、字符串等内置类型的对象,都是第一类对象。而对于函数,在 Python 语言中是第一类对象,但在其他语言中,比如 C、C++语言中就不是第一类对象。
1. 函数名称引用函数对象
下面定义一个极简函数:
>>> def foo(): pass
...
函数 bar() 的函数体中只有 pass ,这是一个 Python 关键词,它也形成一个语句,表示在当前函数体中什么事情也不做。
>>> foo
<function foo at 0x7fe70c6424c0>
foo 是函数名称,从上述返回结果可知,这个名称就引用(代表)了函数对象,<function foo at 0x7fe70c6424c0> 说明函数作为一个对象存在于内存之中,其内存地址是 0x7fe70c6424c0 (以十六进制表示)。如果与一种内置对象对比,比如 x = 3 ,其中的 3 是对象,变量 x 引用了此对象;此处的 foo 的作用与 x 相当,它是函数的名称,引用了函数对象。
2. 函数对象用于赋值语句
>>> y = foo # (1)
>>> y
<function foo at 0x7fe70c6424c0>
通过注释(1)的赋值语句——注意,不能写成 y = foo() ——变量 y 也引用了 foo 所引用的函数对象。下面是我们熟知的验证方法:
>>> id(foo)
140630322062528
>>> id(y)
140630322062528
>>> hex(id(foo)) # 转化为十六进制
'0x7fe70c6424c0'
>>> hex(id(y))
'0x7fe70c6424c0'
>>> type(foo)
<class 'function'>
>>> type(y)
<class 'function'>
由此我们可以进一步理解“名称引用对象”的提法,不论是变量名称还是定义函数是所用的函数名称,在注释(1)中,它们引用了同样的对象,并且该对象能够被调用(关于对象的“可调用”,参阅第9章9.5.1节)。为了更清晰地理解“对象能够被调用”的效果,写一个能够打印信息的函数。
>>> def bar():
... print('calling a function')
...
>>> bar()
calling a function
再通过赋值语句,用另外一个变量引用上述定义的函数对象。
>>> z = bar
名称 z 与名称 bar 引用了同一个函数对象,既然前面用 bar() 执行了此函数对象,那么 z() 也应该实现同样的操作效果。
>>> z()
calling a function
果然如此。
在 Python 里有个统一的规定,只要在对象后面写上圆括号 ( ) ——有的时候圆括号中需要提供参数,就表示调用或者执行该对象。
当然,并非所有对象多能被调用,例如:
>>> 3()
<stdin>:1: SyntaxWarning: 'int' object is not callable; perhaps you missed a comma?
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
在异常信息中就明确告知,整数类型对象不能调用——其原因请参阅第9章9.5.1节。
在本书中,通常用 bar() 这种形式表示函数,除了是跟普通变量名称区别之外,也是为了表示该对象可调用。
3. 函数对象作为参数
函数的实参,可以是任何 Python 对象,那么作为第一类对象的函数,自然也可以作为实参。
>>> def inner_func():
... print("this is inner function.")
...
>>> def out_func(f):
... f()
...
函数 inner_func() 很平常,函数 out_func() 则不同以往,特殊之处就是函数内的 f() ,根据7.3.1所学可知,这是表示要执行 f 引用的对象。那么,在调用 out_func() 函数的时候,必须让 f 引用一个能够像这样 f() 执行的对象,否则就会报错,比如:
>>> out_func(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in out_func
TypeError: 'int' object is not callable
哪个函数适合呢?近在眼前:
>>> inner_func()
this is inner function.
于是将此函数对象传给 out_func() ,即 inner_func 引用的函数对象做实参。
>>> out_func(inner_func) # (2)
this is inner function.
注释(2)中,将 inner_func 作为函数 out_func() 的参数——不要写成 inner_func() 。
不仅可以用自己定义的函数对象,还可用任何其他对象,只要能够用 f() 样式调用即可。
>>> lst = [1, 2, 3, 4]
>>> out_func(lst.pop) # (3)
>>> lst
[1, 2, 3]
lst.pop 也是函数对象——严格说是列表的方法,类似函数,也是对象。lst.pop() 能够删除列表最后一个成员,注释(3)就执行了此对象(没有显示返回值,是因为函数中没有将返回值用变量引用和打印),将列表的最后一个成员删除了。
还可以:
>>> out_func(list)
函数 list() 是内置函数,它的名称 list 也同样引用的是该函数对象,list() 则是执行此函数对象,并创建空列表。
上述示例说明,函数对象可以作为函数的参数。