函数对象
在 Python 中,函数是一等对象(first-class object)。这意味着函数可以像整数、字符串、列表一样被赋值、存储、传递、返回。理解函数的对象本质,是掌握 Python 函数式编程和高级特性的基础。
函数的属性
函数对象有许多内置属性:
def greet(name):
"""打印问候语。"""
print "Hello,", name
print type(greet) # <type 'function'>
print greet.__name__ # 'greet'
print greet.__doc__ # '打印问候语。'
print greet.__module__ # '__main__'
# 查看参数信息
import inspect
print inspect.getargspec(greet) # ArgSpec(args=['name'], varargs=None, keywords=None, defaults=None)
赋值给变量
def square(x):
return x ** 2
my_func = square # my_func 和 square 指向同一个函数对象
print my_func(5) # 25
print square(5) # 25
赋值后,my_func 和 square 完全等价。调用 my_func 就是调用 square。
存储在数据结构中
def add(x, y):
return x + y
def subtract(x, y):
return x - y
def multiply(x, y):
return x * y
# 存储在字典中
operations = {
"+": add,
"-": subtract,
"*": multiply,
}
op = "+"
result = operations[op](3, 5)
print result # 8
# 存储在列表中
funcs = [add, subtract, multiply]
for f in funcs:
print f(10, 5)
# 15, 5, 50
这种"函数表"模式是替代长 if-elif 链的优雅方式。
作为参数传递
def apply_operation(nums, operation):
"""对列表中的每个元素应用操作。"""
return [operation(n) for n in nums]
def double(x):
return x * 2
def square(x):
return x ** 2
nums = [1, 2, 3, 4]
print apply_operation(nums, double) # [2, 4, 6, 8]
print apply_operation(nums, square) # [1, 4, 9, 16]
从函数返回
def make_power(exponent):
"""创建求幂函数。"""
def power(base):
return base ** exponent
return power
square = make_power(2)
cube = make_power(3)
print square(5) # 25
print cube(3) # 27
返回的函数"记住"了创建时的 exponent 值,这就是闭包(closure)。
闭包
闭包是函数式编程的核心概念。当一个嵌套函数引用了外部函数的变量,并且这个嵌套函数被返回或传递到外部时,它就形成了一个闭包——即使外部函数已经执行完毕,嵌套函数仍然可以访问那些变量。
def make_counter():
count = [0] # 用列表包装,实现可变闭包变量
def counter():
count[0] += 1
return count[0]
return counter
c1 = make_counter()
c2 = make_counter()
print c1() # 1
print c1() # 2
print c2() # 1 —— 独立的计数器
print c1() # 3
每个调用 make_counter() 都创建独立的闭包环境,c1 和 c2 互不影响。
注意:Python 2 中闭包变量不能直接赋值(会创建局部变量),需要用可变对象(如列表)包装:
# 错误:Python 2 中不能修改闭包变量
def make_counter_bad():
count = 0
def counter():
count += 1 # UnboundLocalError!
return count
return counter
# 正确:用列表包装
def make_counter_good():
count = [0]
def counter():
count[0] += 1 # 修改列表内容,不是重新赋值
return count[0]
return counter
Python 3 引入了 nonlocal 关键字来解决这个问题。
函数的内省
可以用 inspect 模块深入了解函数:
import inspect
def example(a, b=2, *args, **kwargs):
pass
print inspect.getargspec(example)
# ArgSpec(args=['a', 'b'], varargs='args', keywords='kwargs', defaults=(2,))
print inspect.isfunction(example) # True
print inspect.isbuiltin(len) # True
函数 vs 方法
当函数作为类的属性时,它变成了方法:
class Calculator(object):
def add(self, x, y):
return x + y
calc = Calculator()
print type(calc.add) # <type 'instancemethod'>
print calc.add(3, 5) # 8,self 自动传入
方法对象在调用时会自动把实例作为第一个参数(self)传入。这是 Python 面向对象机制的基础。